谁能向我解释以下代码段在运行期间会发生什么,为什么会输出33?
谢谢,
#include <stdio.h>
void main(){
int* p = (int*)17;
printf("%d\n", (int)(long)(p+4));
}
最佳答案
p
是指向int
或某些int
的指针。
您将其初始化为指向地址17。(由于几个原因,这是一个大问题,我们将解决。)
然后,您将添加4。显然,您计算机上的sizeof(int)
为4,即int
占用4个字节(32位)。当将4加到int
指针时,编译器知道您想使其更指向4 int
的值,因此编译器将4×4 = 16添加到该地址。现在p
指向地址33。
然后,您将p
从指针“投射”(转换)到long
。所以现在,它不再是指向地址33的指针,而只是数字33。
然后,再次将其从long
投射到int
。这基本上将其从33转换为33。(如果您计算机上的long
类型大于4字节,则此转换可能涉及到从64位值33转换为32位值33的情况,即,从0x0000000000000021
到0x00000021
。)
然后,使用int
打印出最后一个%d
值。所以您看到数字33。
现在,通常说
int* p = (int*)17;
这是一个坏主意,因为您有一个指针,该指针的值可能无法使用。通常,使用指针做的一件事是操纵它们指向的值。但是如果你要说
printf("value pointed to by p is %d\n", *p);
您最终将尝试从内存中的地址17获取
int
值。但是(a)您可能没有权限从地址17读取数据,并且(b)17不是4的倍数,因此即使您选择了以下地址,您的处理器也可能甚至不愿意尝试从该地址获取int
确实有许可。因此,这段代码几乎肯定会崩溃。但是,由于在您的代码中,您实际上从未尝试对
int
假定由p
指向的p
进行任何操作(在添加4之前或之后),您的代码似乎-几乎不会- “工作”。一方面,这是不好的,不可移植的,很难定义的代码,如果不是完全未定义的代码。但话又说回来,它可能并不实用(显然没有人会运行它来完成任何实际工作)。因此,如果我们从中获得的教训只是关于指针算术工作原理(特别是编译器如何根据指向的对象的大小自动对其进行缩放)的教训,那么也许我们不必花费太多时间迷惑它的许多丑陋和缺陷。 (在注释线程中,关于代码是未定义还是仅是实现定义存在一点争议,但只要您注意不要真正编写这样的代码,就不必担心区别。)
如果您想或多或少地学习与指针算术相同的课程,但是使用更合理且更便于携带的程序,请尝试以下操作:
#include <stdio.h>
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int main()
{
int *p = &a[3];
printf("p is %p and points to %d\n", p, *p);
printf("p+4 is %p and points to %d\n", p+4, *(p+4));
}
在这里,我们没有将像17这样的人工值填充到
int
中,而是将其初始化为指向实际数组,这正是指针的目的。而且,我们不是将指针值转换为%d
并使用%p
打印它,而是使用旨在打印指针的0x10f47a03c
打印它。在我的机器上,该程序会打印
p is 0x10f47a02c and points to 3
p+4 is 0x10f47a03c and points to 7
如您所见,指针值不是容易消化的小数字,例如17和33,而我的机器选择以十六进制打印它们。但是,很容易验证
0x10f47a02c
-0x10
是int
还是16。我们添加了4,表示“使其指向现在指向的第4个printf
”,并且编译器添加了16。[脚注。我说这是“大部分可移植的”。为了使其完全可移植,您必须将
%p
调用更改为 printf("p is %p and points to %d\n", (void *)p, *p);
printf("p+4 is %p and points to %d\n", (void *)(p+4), *(p+4));
严格来说,
void
仅定义为打印指向(void *)
的通用指针,而不是任意其他指针类型。因此,严格来说,我们需要这些关于c - 任何人都可以向我解释以下内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50181229/