C语言类型的本质(五):指针

指针作为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

前文介绍过*&&用来获取变量的存储首地址,*可以通过首地址解析出内存块,内存块既然是块,必然得有边界,内存作为一维空间,通过上下边界就可以圈定出一个块,上下边界对应着首地址和尾地址。首地址一般由指针变量提供,仿照前文的计算方式,还需要一个内存空间占用才能计算出尾地址,所有指针类型的读取器、存储器和空间占用都是一样的,相较于普通变量,指针格式增加了一个基本格式属性,用来描述当前指针类型依附于哪种格式,解析地址的时候就会根据基本格式的空间占用来计算出尾地址,从而得到整个内存块。下面通过几个例子详细说明依附格式到底怎么理解。

TODO.....