参考 The C Programming Language 2nd Edition section 5.1 ~ 5.5
理解C语言指针
通过 pointers.c
文件的输出辅助理解C指针
pointers.c 源码
#include <stdio.h>
#include <stdlib.h>
void
f(void)
{
int a[4];
int *b = malloc(16);
int *c;
int i;
printf("1: a = %p, b = %p, c = %p\n", a, b, c);
c = a;
for (i = 0; i < 4; i++)
a[i] = 100 + i;
c[0] = 200;
printf("2: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
c[1] = 300;
*(c + 2) = 301;
3[c] = 302;
printf("3: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
c = c + 1;
*c = 400;
printf("4: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
c = (int *) ((char *) c + 1);
*c = 500;
printf("5: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
a[0], a[1], a[2], a[3]);
b = (int *) a + 1;
c = (int *) ((char *) a + 1);
printf("6: a = %p, b = %p, c = %p\n", a, b, c);
}
int
main(int ac, char **av)
{
f();
return 0;
}
输出信息即其分析
# 首先输出数组的首个元素的地址、两个指针的地址
1: a = 0x7ffe1a866ce0, b = 0x56346c0d0260, c = 0xf0b5ff
# c指向数组首地址,通过c[0]=200改变a[0],其他元素就是 100+index
2: a[0] = 200, a[1] = 101, a[2] = 102, a[3] = 103
# 通过指针c改变数组a的值
3: a[0] = 200, a[1] = 300, a[2] = 301, a[3] = 302
# 经过c=c+1后,c指向数组第2个元素,即a[1]
4: a[0] = 200, a[1] = 400, a[2] = 301, a[3] = 302
# 见下具体分析
5: a[0] = 200, a[1] = 128144, a[2] = 256, a[3] = 302
# b是int型指针运算,每次跳转4个字节,因此b-a=0x4。将a强转为char指针后进行算术运算,跳转1个字节,所以c-a=0x1,并且将此内存的值当作指向int型的指针
6: a = 0x7ffe1a866ce0, b = 0x7ffe1a866ce4, c = 0x7ffe1a866ce1
第5行具体分析
为了深入分析每个内存地址存放的值,首先需要确定操作系统的内存是大端模式还是小端模式。
大端模式是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中。小端模式反之。
通过一个C程序来测试
#include <stdio.h>
int main() {
int i = 0x12345678;
char *p = (char *) &i; // p 指向 i 的低地址
if (*p == 0x12) { // 低地址存放高位数据
printf("The system is big-endian.\n");
} else if (*p == 0x78) { // 低地址存放低位数据
printf("The system is little-endian.\n");
} else {
printf("Unknown endianness.\n");
}
return 0;
}
// 最终测试可以发现是小端模式
接下来深入到内存进行分析
a
的地址为 0x7ffe1a866ce0
,经过 c=a
和 c = c + 1
的操作后其地址为 0x7ffe1a866ce4
c = (int *) ((char *) c + 1);
操作会导致 c
的地址为 0x7ffe1a866ce5
。对它的操作会影响包括此地址在内的四个字节 0x7ffe1a866ce5~0x7ffe1a866ce8
,所以会影响 a[1] 和 a[2]
首先计算 a[1] = 400 = 0x00000190
, a[2] = 301 = 0x0000012D
列出 ``&a[0] = 0x7ffe1a866ce4 至
&a[2] - 0x1 = 0x7ffe1a866ceb 经过
*c=500 = 0x000001f4 操作前后内存内存放的值(篇幅限制每个内存地址仅写最后四位。
–` 表示不会影响。(记住是小端模式,所以低位数据填在低地址)
6ce4 | 6ce5 | 6ce6 | 6ce7 | 6ce8 | 6ce9 | 6cea | 6ceB |
---|---|---|---|---|---|---|---|
90 | 01 | 00 | 00 | 2D | 01 | 00 | 00 |
– | f4 | 01 | 00 | 00 | – | – | – |
所以此时 a[0] = 0x0001f490 = 128144, a[1] = 0x00000100 = 256