07- c语言指针 (C语言)

news2024/12/28 18:07:25

一 指针的引入

1、一般把内存中的一个字节称为一个内存单元。
2、为了正确地访问这些内存单元,必须为每个内存单元编上号。根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址,通常也把这个地址称为指针
3、如果在程序中定义了一个变量,在对程序进行编译或运行时,系统就会给这个变量分配内存单元,并确定它的内存地址(编号)。
4、变量的地址就是变量的指针,存放变量地址的变量是指针变量
5、内存单元的指针内存单元的内容是两个不同的概念。可以用一个通俗的例子来说明它们之间的关系。我们到银行去存取款时,银行工作人员将根据我们的帐号去找我们的存款单,找到之后在存单上写入存款、取款的金额。在这里,帐号就是存单的指针,存款数是存单的内容。对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该单元的内容。

二 指针变量的定义和使用

2.1 指针变量定义语法

数据类型  *指针变量名,注意:
1、数据类型为C语言支持的 所有数据类型
2、指针变量名遵循C语言变量的 命名规则

举例:

int *p; //定义了一个指针变量p,简称指针p,p是变量, int *是类型
char* p2;

我们也认为 指针是一种数据类型

2.2 指针变量的赋值

1)指针变量的值代表这个指针指向了以这个值为首地址的那块 内存空间

2)指针变量赋值 为其他变量的地址:&:取地址运算符

//指针变量 = &变量名;
&:取地址运算符
int a = 10;
int *p = &a;

3)指针变量 操作指向的内存空间
可以通过 指针变量访问 和修改所指向的内存空间中的内容

*:指针运算符(或称“间接访问” 运算)。
int a = 10;
int *p;
p = &a;
printf("*p: %d\n", *p);
*p = 100;
printf("*p: %d\n", *p);

4)指针变量同普通变量一样,定义后如果不进行初始化指针变量的值是不确定的

5)野指针
因为指针变量的值是不确定的,所以这个指针我们称之为“野指针”。
野指针的危害:因为指针指向的空间是不确定的,所以指针可能会操作到非法的内存空间,导致程序运行奔溃。 

int a = 100;
int *p;
*p = 1000;
/*因为p没有初始化/赋值,所以p的值是不确定的,如果此时p的值恰好等于a的地址(p == &a), 那么*p=1000将a的值修改为如果p的值恰好是内存上一块只读的内存空间,*p = 1000将导致程序异常退出,你可能会看到程序运行报错(段错误/核心内容*/

6)空指针
为了标志指针变量没有指向任何变量(空闲可用),C语言中,可以把NULL赋值给此指针,这样就标志此指针为空指针。

int *p = NULL;

NULL是一个值为0的宏常量:

#define NULL ((void *)0)

注意:空指针的作用是防止指针变量变成野指针。如果用*访问空指针所指向的内存空间也会程序报错。

7)笔试题:嵌入式系统经常具有要求程序员去 访问某特定的内存位置 的特点。在某工程中,要求设置一绝对地址为 0x67a9 的整型变量的值为 0xaa66

//方法1:
int *ptr;
ptr = (int *)0x67a9; //在内存地址编号的前面加上(int *)将地址编号这个无符号整型数据强制转换为
//(int*)指针类型,这样赋值符号左值和右值的数据类型一致
*ptr = 0xaa66;
//方法2:
*(int *)(0x67a9) = 0xaa55;

注意:在实际工作中我们一般 很少会将一个确定的内存地址赋值给一个指针变量,因为程序员一般不知道哪个内存地址是可用的!!!

2.3 不同类型指针变量之间的区别

1、int *p1 和 char *p2 的相同点是什么?

int x = 100;
int *p1 = &x;
char y = 'A';
char *p2 = &y;

相同点:

  • 都是指针变量
  • 都是用来保存一个内存地址编号
  • 占用的内存空间大小一样
int *p1;
char *p2;
printf("%d %d\n", sizeof(p1), sizeof(p2));

我们发现p1和p2占用的内存空间为4/8,在32位机器上结果为4, 64位机器上结果为8。
思考:

为什么指针变量占用的内存空间是 4 或者 8个字节呢?
因为指针变量保存的是一个内存地址的编号!
32位机器内存地址编号最大值为 2^32-1,可以用一个4字节的变量保存。
64位机器内存地址编号最大值为 2^64-1,可以用一个8字节的变量保存。

2、int *p1 和 char *p2 的不同点是什么?
首先我们应该知道:内存中存储的只是二进制而已。
之所以有 int float char 等数据类型是程序员希望将存储在内存中的二进制当作某种数据类型来处理而已。

int x = 65;
printf("%c\n", x);
  • int *p1的作用就是指针变量p1将他所指向的内存空间中的二进制当作 int类型 来处理。
  • char *p2的作用就是指针变量p2将他所指向的内存空间中的二进制当作 char类型 来处理。

3、p1++ 和 p2++的区别

#include <iostream>

int main() {
    int x = 10;
    int *p1 = &x;
    char y = 'A';
    char *p2 = &y;
    printf("p1: %p, p2: %p\n", p1, p2);
    p1++;
    p2++;
    printf("p1: %p, p2: %p\n", p1, p2);
    return 0;
}

p1自增后和自增前值 相差4
p2自增后和自增前值 相差1
指针变量+n,不是指针往后偏移n个字节,而是指针变量往后偏移n个数据类型,例如:p1+3,
表示指针p1往后偏移3个int类型的数据,指针变量p1的值+12 (3*sizeof(int)) 。

三 指针和数组

3.1 数组的指针

1、一个变量有一个地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。所谓 数组的指针是指数组的起始地址
2、数组名表示数组的首地址,因此数组名也是一种指针
3、通过数组名访问数组中元素。

int ch[] = {1,2,3,4};
//假如我们想访问ch中的第3个元素:ch[3] == 4
//我们也可以通过指针法引用数组中的元素:比如 *(ch + 3)
//那么,如果想访问第n个元素呢?
*(ch + n) 注意:n<=sizeof(ch)/sizeof(ch[0])-1

4、练习:假如有数组int a[4], 编写代码实现如下功能:

  • 通过从键盘上输入数字对数组a的每一个元素进行赋值
  • 打印出数组a中每一个元素的地址
  • 通过指针法将数组a中的每一个元素的值打印出来
#include <iostream>

int main() {
    int a[4];
    int i;
    for (i = 0; i < 4; i++)
        scanf("%d", &a[i]);
    for (i = 0; i < 4; i++) {
        printf("%p %d\\n", &a[i], *(a+i));
    }
    }

5、通过指针变量间接访问数组

#include <iostream>

int main() {
    int a[4] = {1,2,3,4};
    int *p;
    p = a;
    *(p + 2) = 100;
    char ch[] = {'a', 'b', 'c'};
    char *p2;
    p2 = ch;
    *(p2+1) = 'A';
    }
#include <iostream>

int main() {
    int ch[] = {1, 2, 3, 4};
    printf("%d\n", ch[4]);
    printf("%p %p\n", &ch[3], &ch[4]);
    //数组名:数组的首地址
    printf("ch: %p\n", ch);
    //数组中的第0个元素的地址:数组的首地址
    printf("&ch[0]: %p\n", &ch[0]);
    printf("%d %d\n", ch[3], *(ch+3));
    int a[4];
    int i;
    for (i = 0; i < 4; i++)
    {
        scanf("%d", &a[i]); //a+i
        getchar();
    }
    //打印数组中每个元素的地址
    for (i = 0; i < 4; i++)
        printf("%p\n", &a[i]);
    //通过指针法将数组a中的每一个元素的值打印出来
    for (i = 0; i < 4; i++)
        printf("%d\n", *(a+i));
    int *p;
    p = a; //指针p指向了数组a

    //指针指向了一个数组,可以将指针当数组看待
    for (i = 0; i < 4; i++)
        printf("%d\n", p[i]); //通过下标发访问数组中的元素
        // printf("%d\n", *(p+i));
    }

6、数组指针越界

#include <iostream>

int main() {
    int b[4] = {10, 20, 30, 40};
    int a[4];
    a[4] = 100;
    printf("a[4]: %d\n", a[4]);
    printf("b[0]: %d\n", b[0]);
    printf("a: %u, b: %u\n", &a, &b); //打印数组a和b的首地址
    }

3.2 指针数组

1、指针数组顾名思义就是:存放指针的数组,本质是数组,数组中的每个元素都是指针 

#include <stdio.h>

int main()
{
    int a = 10, b = 20, c = 30;
    int *p[3];
    p[0] = &a;
    p[1] = &b;
    p[2] = &c;
    return 0;
}

2、注意:int *p[3]; 等价于 (int *) p[3]; 因为[] 比 *的优先级要高先与p匹配。
3、思考:如何通过一个数组存储10个人的姓名?
char *name[10] = {"zhangsan", "lisi", "wangwu", "zhaoliu, "tianqi"};
name数组中保存了10个字符串常量的首地址(注意:没有保存字符串常量而是常量的首地址)。

3.3 指针变量的地址

1、我们在定义一个 指针变量 的时候,编译器会分配一块空间来存储这个指针变量的值,分配的这块内存空间肯定有一个地址编码啦,那么这个地址编码肯定就是这个指针变量的地址啦。

#include <stdio.h>

int main()
{
    int a = 10;
    int *p;
    p = &a;
    //将指针变量p的值以及变量a的地址打印出来(结果应该是两者相等)
    printf("p: %p, &a: %p\n", p, &a);
    //打印指针变量p的地址(存储指针变量p的内存空间的首地址)
    printf("&p: %p\n", &p);
    return 0;
}

2、强调:指针变量p的值保存的是另外一个变量a的地址0x300800,指针变量的地址是存储p这个指针变量的值的那块内存空间的首地址:0x3007F8,这块空间中保存的值是0x300800 

3.4 一级指针作为函数的形参

1、函数的形参为数组
如果函数的形参是数组,该形参的定义方法如下:

void func(int a[], int n)
{}

我们也可以将形参定义为 指针类型
 

void func(int *a, int n)
{}

在实际工作中我们通常使用第二种方法!

2、调用函数时需要传递字符串可将形参设计为char *类型

#include <stdio.h>

void func(char *p) //调用函数时将字符串的地址赋值给指针变量p
{
    printf("%c\n", p[0]);   //返回值为 'h'
}
int main()
{
    func("hello");
    return 0;
}

3、当形参为数组时,如果获取数组的长度呢?     //sizeof( )

为什么sizeof(a)和sizeof(b)的值都是8呢?
原因:编译器在编译的时候将a和b当做了指针来处理了!!! 

4、注意:如果函数的 形参为指针,在函数体中一般先对指针的值进行判断,判断指针的值是否为NULL

3.5 二级指针

1、用一个指针变量保存一个一级指针变量的地址,这个指针我们称之为二级指针
2、二级指针的定义 :

  • 数据类型 **变量名;

3、二级指针的应用 

void func_1(){
    //二级指针的使用
    int a = 10;
    int *p = &a;
    int **p2 = &p; //二级指针p2保存了一级指针p的地址(p2指向了p )
    int ***p3 = &p2; //三级指针
    //*p2 == p == &a
    printf("%p %p %p\n", *p2, p, &a);  //000000000061FDE4 000000000061FDE4 
    //**p2 == *p == *(&a) == a
    printf("%d %d %d %d\n", **p2, *p, *(&a), a);   //10 10 10 10

    **p2 = 100;
    printf("%d %d %d %d\n", **p2, *p, *(&a), a);  //100 100 100 100
    //*p3 == p2 = &p
    //**p3 == *p2 == p == &a
    //***p3 == **p2 == *p == *(&a) == a
    printf("***p3: %d\n", ***p3);   //***p3: 100
}

3.6 内存分配

1、在实际工作中,如果我们需要存储多个数据很多同学首先想到的是使用数组,但是因为数组的长度在定义完后是固定的所以往往不够灵活
2、我们可以根据需要存储的数据类型先定义一个指针变量,例如:int *p; 然后根据实际需求使用
malloc函数
分配空间。

3、malloc函数

#include <stdlib.h>
void *malloc(size_t size);

功能:malloc 函数像系统申请size个字节的内存空间,并且返回一个指针,这个指针指向被分配的内存空间的首地址,并且申请的内存空间是在“堆”上的。堆上的空间是需要手动申请,手动释放的!!否则就会造成内存泄漏。

int *p;
//假如我们需要存储10个int类型的数据
p = (int *)malloc(10*sizeof(int));

注意:分配的空间为 10*sizeof(int), 因为malloc申请分配的空间是以字节为单位的。

int *p;
//通过指针变量p操作一块空间,可以存储4个int数据
//向系统申请 4*sizeof(int)字节的内存空间
p = (int *)malloc(4*sizeof(int)); //在堆上申请了4*sizeof(int)字节的内存空间
//p的值:申请到的堆上的内存空间的首地址 (指针p指向申请到的堆上的空间)
printf("p: %p\n", p);
//通过指针变量p 来操作申请到的堆空间
p[0] = 100;
p[1] = 200;
*(p+2) = 300;
*(p+3) = 400;

4、内存释放:free函数 

#include <stdlib.h>
void free(void *ptr);

功能:释放ptr所指向的内存空间

注意:
free函数并不会修改指针变量的值!但是free执行完成以后指针所指向的原来的那块地址空间中的内容是不确定的!!
问题:
释放空间到底做了什么事情呢?
最重要的是:告诉系统这块内存空间可以给别人使用了!!!

  • malloc 所在的源文件: #include <stdlib.h>
  • strcpy 所在的源文件: #include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]){
    char *p;
    //malloc分配的空间是在堆上的,需要手动释放
    p = (char *)malloc(10);
    strcpy(p, "hello");
    printf("p所指向的空间的内容: %s\n", p); //结果是hello

    //将p所指向的地址空间的首地址打印出来(就是将指针变量p的值打印出来)
    printf("p的值: %p\n", p);
    free(p);
    //将p所指向的地址空间的首地址打印出来
    printf("p的值: %p\n", p);

    //仔细观察,下面这条打印语句的结果
    printf("p所指向的空间的内容: %s\n", p);  //结果不是hello

    strcpy(p, "world");
    //再仔细观察,下面这条打印语句的结果
    printf("p所指向的空间的内容: %s\n", p);  //结果是world

    return 0;
}

free函数调用完以后的使用技巧: 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]){
    char *p;
    //malloc分配的空间是在堆上的,需要手动释放
    p = (char *)malloc(10);
    strcpy(p, "hello");
    
    //释放申请的内容
    free(p);
    p = NULL;
    return 0;
}

5、如果之前分配的空间不够了怎么办呢?
我们可以使用 realloc函数: 

#include <stdlib.h>
void *realloc(void *ptr, size_t size);

功能:在堆上分配一块size所指定的新的内存空间,空间大小单位为字节,并且还会将ptr所指向的空间中的内容拷贝到新的内存空间中,最后返回新的内存空间的额首地址
示例代码: 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char *p;
    p = (char *)malloc(10);
    strcpy(p, "hello"); //向分配的空间中拷贝字符串
    printf("p所指向空间的首地址: %p\n", p);
    printf("p所指向空间的内容: %s\n", p);
    p = (char *)realloc(p, 20); //重新分配新的空间
    printf("p所指向新的空间的首地址: %p\n", p);
    printf("p所指向新的空间的内容: %s\n", p);
    //注意:分配的新的空间的首地址有可能有之前分配的空间首地址一样,也有可能不一样
    strcat(p, " world"); //追加字符串
    printf("p所指向新的空间的内容: %s\n", p);
    return 0;
}

6、思考一种情景,char *dest, *src; 通过一个函数将src所指向的地址空间中的内容拷贝到dest所指向的地址空间中, 但是假设在调用函数前我们并不知道src的长度,这个时候我们需要将函数的形参设计为 二级指针

void test2(char **dest, char *src){
    //通过二级指针dest给形参一级指针dest分配内存空间!
    *dest = (char *)malloc(strlen(src) + 1);
    if (NULL == *dest || NULL == src)
        return ;
    strcpy(*dest, src);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void func4(char **dst)
{
    //*dst == p
    *dst = (char *)malloc(10); //在堆上申请了10个字节
    strcpy(*dst, "hello");
}

int main()
{
    char *p; //指针指向某个函数调用结束后 在函数体中申请的堆空间的首地址
    /*
    * 既然我希望让p指向一块堆空间,其实就是希望对p进行赋值,赋值为在函数中申请的堆空间的首地址
    * 如何在函数中对p进行赋值呢?必须在调用函数的时候传递p的地址!!!!
    * */
    func4(&p);
    printf("%s\n", p);
    free(p);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/680845.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

车载网络测试 - CANCANFD - 基础篇_03

十、发送方式与过滤方式 1、广播发送及规则 我们以小组讨论现场为例来说明CAN总线广播发送规则&#xff1a; 1&#xff09;一个房间代表同一路CAN总线&#xff0c;每一个小组代表一个CAN Node&#xff0c;每一个小组成员发言代表发送一帧CAN报文&#xff0c;对所有的小组成员进…

生成对抗网络

1 GAN基本概念 1.1 GAN介绍 GAN的英文全称是Generative Adversarial Network&#xff0c;中文名是生成对抗网络。它由两个部分组成&#xff0c;生成器和鉴别器&#xff08;又称判别器&#xff09;&#xff0c;生成网络&#xff08;Generator&#xff09;负责生成模拟数据&…

【Python】异常处理 ④ ( 异常处理 else 语句 | 异常处理 finally 语句 )

文章目录 一、Python 异常捕获 else 语句1、异常捕获 else 语句2、代码示例 - 没有触发 else 语句的情况3、代码示例 - 触发 else 语句的情况 二、Python 异常捕获 finally 语句1、异常捕获 finally 语句2、代码示例 - 出现异常后执行 finally 语句 一、Python 异常捕获 else 语…

展示和标注图像:探索Gradio AnnotatedImage模块的功能

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

代码生成器原理分析

代码生成器原理分析 理解代码生成器的需求和实现思路掌握freemaker的使用 理解数据库中的元数据完成环境搭建工作 浅谈代码生成器 概述 在项目开发过程中&#xff0c;关注点更多是在业务功能的开发及保证业务流程的正确性上&#xff0c;对于重复性的代码编写占据了程 序员…

指标综合评价(定性指标定量化、指标正相关化、赋权重)

目录 一、定性指标定量化 二、将指标同型化 线性比例变换法 极差变换法 二、评价指标赋予权重 三、综合评价 战斗机性能的综合评价问题 例&#xff1a;战斗机的性能指标主要包括最大速度、飞行半径、最大负载、隐身性能、垂直起降性能、可靠性、灵敏度等指标和相关费用。…

Vue+Element UI 生鲜管理系统简介及项目搭建,页面布局(一)

文章目录 浅谈一、背景二、搭建创建vue项目vue项目结构简介安装Element UI库安装axios安装querystring安装normalize.css安装echarts运行删除无用组件基础css样式导入 三、页面布局配置路由布局flex布局&#xff08;弹性盒子&#xff09;固定布局固定布局配置路由 浅谈 自从入…

【Rust】2、实战:文件、网络、时间、进程-线程-容器、内核、信号-中断-异常

文章目录 七、文件和存储7.2 serde 与 bincode 序列化7.3 实现一个 hexdump7.4 操作文件7.4.1 打开文件7.4.2 用 std::fs::Path 交互 7.5 基于 append 模式实现 kv数据库7.5.1 kv 模型7.5.2 命令行接口 7.6 前端代码7.6.1 用条件编译定制要编译的内容 7.7 核心&#xff1a;LIBA…

【Java高级语法】(十五)lambda表达式:给你一颗语法糖Lambda,解析函数式编程的杰作~

Java高级语法详解之lambda表达式 1️⃣ 概念2️⃣ 优势和缺点3️⃣ 使用3.1 语法结构3.2 案例3.2.1 无参Lambda3.2.2 带有一个参数3.2.3 带有多个参数3.2.4 方法引用的简化形式 4️⃣ 应用场景5️⃣ 优化技巧6️⃣ 原理7️⃣ 注意性能问题&#x1f33e; 总结 1️⃣ 概念 Java …

架构设计第十一讲:架构之高并发:限流

架构设计第十一讲&#xff1a;架构之高并发&#xff1a;限流 每个系统都有服务的上线&#xff0c;所以当流量超过服务极限能力时&#xff0c;系统可能会出现卡死、崩溃的情况&#xff0c;所以就有了降级和限流。限流其实就是&#xff1a;当高并发或者瞬时高并发时&#xff0c;为…

rabbitmq第三课-RabbitMQ高级功能详解以及常用插件实战

一、选择合适的队列. 实际上是可以选择三种队列类型的&#xff0c;classic经典队列&#xff0c;Quorum仲裁队列&#xff0c;Stream流式队列。 后面这两种队列也是RabbitMQ在最近的几个大的版本中推出的新的队列类型。3.8.x推出了Quorum仲裁队列&#xff0c;3.9.x推出了Stream流…

MyBatis何时使用一级缓存,何时使用二级缓存?

Mybatis设计2级缓存来提升数据检索效率&#xff0c;避免每次都查询数据库。 一、一级缓存 一级缓存 Mybatis 的一级缓存是指 SQLSession&#xff0c;一级缓存的作用域是 SQlSession , Mabits 默认开启一级缓存。 在同一个SqlSession中&#xff0c;执行相同的SQL查询时&#x…

基于STM32CUBEMX驱动TOF模块VL6180与VL6180X(2)----修改测量范围

概述 当使用VL6180传感器进行测距时&#xff0c;可以通过修改缩放因子来改变可测量的距离范围。VL6180是一种基于飞行时间原理的传感器&#xff0c;通过测量光信号的往返时间来确定物体与传感器之间的距离。 默认情况下&#xff0c;VL6180传感器的测距范围约为0至200毫米。然…

显卡检测工具:GPU-Z

今天小编为大家测试了一款轻量级的GPU显卡的测试工具&#xff0c;可以查看GPU的详细信息&#xff0c;以供各位同学们学习。 一、简单介绍 GPU-Z是一款方便实用的软件工具&#xff0c;专门为用户提供视频卡和GPU的详尽信息。它具有轻巧的特点&#xff0c;不需要安装即可使用&am…

2023版智慧高速智慧公路总体建设方案,售前人员必备方案

导读&#xff1a;原文《智慧高速智慧公路总体建设方案》共83页PPT&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 如需获取完整的电子版内容参考学习 您可以关注评论…

【雷达原理】基本雷达方程的推导

基本雷达方程 一、研究目的二、推导过程1、基本雷达方程常用的表达形式2、计算案例3、仿真代码 参考文献 一、研究目的 雷达方程定量地描述了作用距离与雷达参数及目标特性之间的关系。 研究雷达方程主要有以下作用&#xff1a; &#xff08;1&#xff09;根据雷达参数来估算雷…

慕课:笔记

课程链接&#xff1a;直面JavaScript中的30个疑难杂症_JavaScript面试题-慕课网 第二章&#xff1a;数据类型 数据类型是每门编程语言的必修之课&#xff0c;你是否对JavaScript的数据类型和检测存在困惑&#xff0c;本章节将为你揭晓其中的奥秘&#xff0c;让你对数据类型有…

矩阵压缩算法

当矩阵中存在着重复元素时&#xff0c;为了节省空间会采用压缩算法&#xff0c;关键在于原矩阵空间与压缩后数据结构的对应&#xff1b; 1.对称压缩&#xff1a;数据沿对角线对称的情况&#xff1b; 将矩阵压缩为一维数组&#xff0c;数组的长度是&#xff1a; 对于num[n][n…

VMware虚拟机暴露端口至公网方法流程详解

目录 需求背景 解决方法 准备工作 虚拟机ip设置方法 需求背景 一台电脑需要连接另一台电脑上的虚拟机的端口&#xff0c;直接ping是无法ping通的&#xff0c;因为本地虚拟机的端口未暴露至公网。 解决方法 虚拟机&#xff1a;CentOS 7 64 Linux 本机&#xff1a;Window…

C专家编程 —— 运行时数据结构

文章目录 代码和数据段代码与可执行文件中对应的位置可执行文件中的段在内存中的布局加入动态链接库的内存空间布局堆栈段的作用过程活动记录函数调用过程记录举例 static和auto关键字 汇编嵌入C代码 代码和数据 代码和数据的区别可以理解为编译时和运行时的分界线。 代码&…