指针作为C语言的灵魂,有着很多无法替代的作用,最典型的就是处理函数的传参:
code
c
void change1(int temp) { temp = 7; } void change2(int * p) { *p = 7; } int a = 5; change1(a); printf("%d", a); // 5 change2(&a); printf("%d", a); // 7
首先通过这个简单的例子介绍指针最基本的用处,change1
有一个参数,参数类型为int,函数进行传参的时候会产生一份拷贝,函数体内都是对这份拷贝进行操作:change1
函数调用过程可以描述为:
code
c
int a = 5; // 分配空间【0-3】 a = { 空间占用: 4, 存储格式: int存储格式, 读取格式: int读取格式, 存储空间指针: 指向第一步分配好的空间(首地址为0), } // 进入change1部分 // 1. 对传参进行浅拷贝 int temp = a; // 分配空间【4 - 7】 temp = { 空间占用: 4, 存储格式: int存储格式, 读取格式: int读取格式, 存储空间指针: 指向另一块分配好的空间(首地址4), } // 2. 赋值 temp = 7; // 根据首地址4和空间占用偏移得到【4 - 7】,将这块内存改为7 // 3. 结束函数调用,释放内存【4 - 7】,输出a,a的内存块是【0 - 3】,而change1修改的内存是【4 - 7】,这块内存已被丢弃,所以a并没有变化,依旧是5 printf("%d", a); // change2过程 int *p = &a; // 这里同样是创建一个临时变量,因为&a的类型为指针,所以临时变量的类型也得是指针,分配内存【4 - 12】 p = { 空间占用: 8, 基本格式: int, 存储格式: 指针存储格式, 读取格式: 指针读取格式, 存储空间指针: 指向分配好的空间(首地址4), } &a得到a的首地址0,所以【4 - 12】中存放地址0 *a = 7的具体操作为 ① 通过 p->存储空间指针 和 p->空间占用 得到要读取的内存为【4 - 12】,读取这块内存得到地址0 ② 根据 p->基本格式 得到 int->空间占用 为4,返回空间为【0 - 3】 ③ 使用 int->存储格式 将【0 - 3】内存中的数据改为7 // 结束函数调用,释放内存【4 - 12】,输出a,a的内存块为【0 - 3】,这块内存已经被修改为7,所以最中结果输出7
前文介绍过*
和&
,&
用来获取变量的存储首地址,*
可以通过首地址解析出内存块,内存块既然是块,必然得有边界,内存作为一维空间,通过上下边界就可以圈定出一个块,上下边界对应着首地址和尾地址。首地址一般由指针变量提供,仿照前文的计算方式,还需要一个内存空间占用才能计算出尾地址,所有指针类型的读取器、存储器和空间占用都是一样的,相较于普通变量,指针格式增加了一个基本格式属性,用来描述当前指针类型依附于哪种格式,解析地址的时候就会根据基本格式的空间占用来计算出尾地址,从而得到整个内存块。下面通过几个例子详细说明依附格式到底怎么理解。