c-为一个指针所指向的位置赋值会影响另一个指针的值


//foo.c
#include <stdio.h>
int main()
{
int a = 1;
int *pn = &a;
printf("%lldn", (long long int) pn);
char *pc = ((char *) pn ) + 4 * (sizeof(char));
printf("%lldn", (long long int) pn);
*pc = ''; //This assignment changes the value of pn.
printf("%lldn", (long long int) pn);
}

在上述C程序中,字符指针pc指向整数指针pn旁边的位置。分配*pc = ''改变pn的值。

gcc -o foo foo.c
./foo

这是输出:

140736942845732
140736942845732
140736942845696

你能理解为什么分配给*pc会影响pn的值吗?

char *pc = ((char *) pn ) + 4 * (sizeof(char));中,pc被设置为指向pn所指向位置之外的四个字节。pn指向a,它有四个字节长(在C实现中(。因此,我们通常不知道pc现在指向的内存中有什么。编译器可以用它想要的任何方式组织内存:在内存中的a(可能在堆栈上(之后,它可以放pnpc或数据供自己使用,也可以什么都不放。

*pc = '';中,在pc指向的位置放置一个空字符(值为零的字节(。当您使用一个指针写入内存时,该指针已被操作为位于它所取自的对象之外,该行为不是由C标准定义的。在这种情况下,编译器似乎在a之后将pn存储在内存中,并且以这种方式写入pc将零字节写入pn

这里的编译器没有错误。当您在正确地址之外写入时,编译器没有义务提供任何特定行为。

另外,请注意,printf("%lldn", (long long int) pn);不是打印指针的正确方式。要打印指针pn,请使用printf("%pn", (void *) pn);或包括<stdint.h><inttypes.h>并使用printf("%" PRIuPTR "n", (uintptr_t) pn);。对于前一个代码,使用%p,输出的形式由C实现选择。使用后一种代码,您可以对格式进行一些控制;您可以使用PRIuPTR进行十进制输出,使用PRIxPTR进行十六进制输出,还可以添加标志和其他控件(如printf("%#08" PRIxPTR "n", (uintptr_t) pn);(来请求以"0x"开头的十六进制格式和足够的前导零,以便打印八位数字。

我觉得这是个bug。不过,公平地说,代码并不理想,因为我们试图访问无人认领的内存。请注意,如果我们首先分配内存,问题就会消失:

#include <stdio.h>
int main()
{
int a[2] = {1, 1};
int *pn = &(a[0]);
printf("%pn", pn);
char *pc = ((char *) pn ) + 4 * (sizeof(char));
printf("%pn", pn);
*pc = ''; //This assignment changes the value of pn.
printf("%pn", pn);
}

我的系统输出(64位(:

$ gcc -o foo foo.c
$ ./foo
0x7ffffcfa6870
0x7ffffcfa6870
0x7ffffcfa6870

编辑:Eric很清楚pn可能被分配在a旁边的堆栈上;所以这有点道理。谜团解开(

最新更新