MIT6.828-Lab1-Part2-Exercise4-理解C语言指针

 

参考 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=ac = 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

参考资料

  1. cnblog-fatsheep9146