实例分析
取数组首位元素的地址和取数组名、取数组名的地址有啥区别呢?首先看看以下代码,看看你是否知道,printf输出的结果?
// 假设数组a[]起始地址为0x000000
float a[10] = {
0.0f};
float *p1, *p2, *p3;
// 测试1
p1 = &a[0];
p2 = a;
p3 = &a;
printf("%d \n", p1);
printf("%d \n", p2);
printf("%d \n", p3);
// 测试2
p1 = &a[0] + 1;
p2 = a + 1;
p3 = &a + 1;
printf("%d \n", p1);
printf("%d \n", p2);
printf("%d \n", p3);
请问以上两个测试代码,你的答案都对了吗?
// 测试1:输出结果全相同
0x000000
0x000000
0x000000
// 测试2:前两个相同,最后一个十进制值比前者多40
0x000000
0x000000
0x000028
如果你对里面的内部运算过程还不太清楚,不妨接着往下看。这个问题如果没搞清楚,在指针赋值运算和内存取用时,极易出现bug,而且一般不会报错,但是不小心弄混后,隐藏bug较难排查。
测试代码
【数组地址赋值指针后,地址算术运算辨析】下面结合完整测试代码,利用注释循序渐近解释其过程。
float a[10] = {
0.0f};
float *p1, *p2, *p3;
p1 = &a[0]; // 首位元素地址
p2 = a; // 数组名即指向的首位元素,相当于&a[0]
p3 = &a; // 对数组名取地址,虽然地址值一样,但是类型是整个数组
// 以下输出结果相同
printf("%d \n", p1);
printf("%d \n", p2);
printf("%d \n", p3);
p1++; // 往后偏移一个float元素, 等价于 p1 += 1;
p2++; // 计算机一个地址对应存储1个byte内存大小
p3++; // 地址往后偏移4个byte,也就是p3的实际值加4
// 以下输出结果相同
printf("%d \n", p1);
printf("%d \n", p2);
printf("%d \n", p3);
// 类似的,以下输出结果也相同
printf("%d \n", &p1[1]); // 地址往后偏移4个byte,一个float空间大小
printf("%d \n", &p2[1]);
printf("%d \n", &p3[1]); // 本质都是移动的p对应的float类型大小
p1 = &a[0] + 1; // 偏移4个byte
p2 = a + 1; // 偏移4个byte,效果类似于取&a[0]
p3 = &a + 1; // 偏移一个数组大小,40个byte;&a代表的是一个数组类型的指针,而非元素
// 而如果赋给p3后,p3再加1的话,则还是按p3的float类型
// 往后偏移4个byte(1个float大小的空间)
// 以下输出结果不同
printf("%d \n", p1);
printf("%d \n", p2);
printf("%d \n", p3);