我在操作系统课程中遇到了这个问题。 这里 是来自6.828(操作系统)在线课程的代码。它旨在让学习者练习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;
}
我将它复制到一个文件中,使用gcc进行编译,然后得到了这个输出:
$ ./pointer
1: a = 0x7ffd3cd02c90, b = 0x55b745ec72a0, c = 0x7ffd3cd03079
2: a[0] = 200, a[1] = 101, a[2] = 102, a[3] = 103
3: a[0] = 200, a[1] = 300, a[2] = 301, a[3] = 302
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
6: a = 0x7ffd3cd02c90, b = 0x7ffd3cd02c94, c = 0x7ffd3cd02c91
我可以轻松理解1、2、3、4的输出。但是对于5的输出,让我很难理解。特别是为什么a[1]=128144和a[2]=256?
看起来这个输出是下面代码的结果:
c = (int *) ((char *) c + 1);
*c = 500;
我不理解代码
c = (int *) ((char *) c + 1)
的功能。按照定义,
c
是一个指针 int *c
。在第5行输出之前,c
通过 c = a
和 c = c + 1
指向数组 a
的第二个地址。现在,(char *) c
和 ((char *) c + 1)
的含义是什么,然后再看 (int *) ((char *) c + 1)
的含义是什么?
c = (int *) ((char *) c + 1)
只是创建了一个指针,这样做存在风险未定义行为,而*c = 500;
也是未定义行为。我猜那些不能做的人就去教别人做。 - Andrew Henle((char *) c + 1)
指向a[1]
的第二个字节。*c = 500
覆盖了除了a[1]
的第一个字节和a[2]
的第一个字节以外的所有内容。在小端架构中,a[1]
变成了(400 & 0xFF) | (500 << 8)
=128144,而a[2]
变成了300 & ~0xFF
。但正如前面的评论所说,这是未定义行为。 - dimich