我有以下程序,它定义了2个整数和一个指向整数的指针。
#include <stdio.h>
int main() {
int bla=999;
int a=42;
int* pa=&a;
printf("%d \n", *pa);
printf("%d \n", pa);
pa++;
//*pa=666; //runs (no error), but the console is showing nothing at all
printf("%d \n", *pa);
printf("%d \n", pa);
pa++;
//*pa=666; //runs and changes the value of *pa to 666;
printf("%d \n", *pa);
printf("%d \n", pa);
}
输出结果为:
42
2686740
2686744
2686744 //这个值很奇怪,我想
999
2686748
地址对我有意义,但第四个值很奇怪,因为它正是int的地址。有人可以解释这种行为吗?
当我评论* pa = 666(第一个外观)时,控制台什么都没有显示,所以这里有一些错误,但编译器没有显示错误。也许这是因为我的系统上的int的大小,我有一个64位的Windows-os,所以也许int是64位而不是32?并且因为在第二次增量之后* pa值是999而不是第一次增加?
我确信,有很多C程序员可以解释发生了什么:)
答案 0 :(得分:5)
int* pa=&a;
pa
是指向整数的指针,并且定义了访问*pa
。
指针递增后,指针指向某个内存(在p之后),这个内存未由您分配或者您不知道,因此取消引用它会导致未定义的行为。
pa++;
*pa
是UB
编辑:
使用正确的格式说明符打印@haccks指出的指针值%p
答案 1 :(得分:2)
您使用错误的格式说明符来打印地址。这将调用未定义的行为,并且一旦调用UB,所有投注都将关闭。请改用%p
。
printf("%p \n", (void *)pa);
另一个问题是执行pa++;
后,您正在访问未分配的内存和UB的另一个原因。
答案 2 :(得分:2)
输出并不奇怪,可以预料到:main()
中有三个变量,所有这些变量都存储在堆栈中,并且恰好是一个接着一个。其中一个变量是指针本身。因此,当您取消引用第三行中的指针时,您将获得指针本身的当前值。
然而,这个输出是不可预测的,它是未定义的行为:你只允许使用指针算法来访问单个内存对象中的数据,在你的情况下,内存对象只是一个int
。因此,在第一个*pa
之后访问pa++
是非法的,并且该程序可以从该点开始执行任何操作。
更具体地说,不能保证哪些其他变量遵循某个变量,它们遵循哪个顺序,或者根本没有可访问的内存。即使在第一个*pa
之后阅读pa++
也会导致程序崩溃。正如您所见,在许多情况下您将不会遇到崩溃(这很容易调试),但代码仍然深受打击。
答案 3 :(得分:2)
你并不比编译器聪明。
正如另一个答案所说,你所做的是未定义的行为。使用pa
你只是做无意义的,它与定义目标的任何可推算的算法都不对应:它没有意义。
但是我会向你提出一个可能出现的情况。虽然很多都可能是错误的,因为编译器会进行优化。
int bla=999;
int a=42;
int* pa=&a;
这些变量在堆栈上分配。
撰写pa = &a
时,您说“我希望指针pa
等于a
的地址”。
编译器可能已经按顺序或声明分配了内存,这可能会产生类似的结果:
bla
的地址为0x00008880 a
的地址为0x00008884 pa
的地址为0x00008888 当你执行pa++
时,你告诉:将int的指针移动到内存中int的下一个位置。
由于整数是32位,因此您正在进行pa = pa + 4bytes
,即pa = 0x00008888
请注意,偶然!,您可能指向pa
指针的地址。
所以现在指针pa
包含它自己的地址......这非常深奥,可以被称为ouroboros。
然后你再问pa++
... pa = pa + 4 bytes
即pa = 0x0000888c
所以现在你可能正在访问一个未知的内存区域。它可能是访问冲突。如果您想要阅读或写作,这是未定义的行为。
答案 4 :(得分:1)
当您第一次指定指针时,它指向2686740
。指针是一个整数指针,整数使用4个字节(通常,在你的机器上它使用4个字节)。这意味着pa++
会将值增加到4,即2686744
。再做一次导致2686748
如果要查看生成的汇编代码,可以切换局部变量的顺序。代码运行时,排序为a
,pa
,bla
。由于您没有明确控制此排序,因此打印输出被视为未定义
在您第一次执行pa++
指针指向自身之后,这就是您获得&#34;奇怪值的原因&#34;
正如许多其他答案所提到的,这不是指针的好用,应该避免。在这种情况下,您无法控制指针指向的内容。更好地使用指针算法将指向数组的开头,然后执行pa++
指向数组中的下一个元素。您可能遇到的唯一问题是递增超过数组的最后一个元素
答案 5 :(得分:0)
您是否尝试通过指针a
增加*pa
中的值?
如果是,请执行:(*pa)++
。括号是至关重要的,因为它们意味着“获取指针的值”,然后使用该地址递增引用的任何内容。
这与*pa++
完全不同,*pa
只返回{{1}}指向的值,然后递增指针(而不是它引用的东西)。
C语法的一小陷阱。 K&amp; R有几页专门讨论这个问题,我建议你尝试一些例子。