c - 任何人都可以向我解释以下内容

标签 c pointers casting

谁能向我解释以下代码段在运行期间会发生什么,为什么会输出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的情况,即,从0x00000000000000210x00000021。)

然后,使用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-0x10int还是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/

相关文章:

c - arduino uno 红外接收器电机控制

Java BitSet - 放置字符/字符串的包装器

将 int 转换为 double 将返回 #INF00

delphi - Delphi XE中如何通过指针获取记录的类型信息?

c - 我在 C 中的字符串填充函数不起作用?

c - 从 C 中的指针实例化一个新结构

c - 将 uint16_t 变量传递给期望 uint8_t 变量危险的函数

c - 有符号和无符号变量的混合

c - 双向链表中的注入(inject)函数在调用 pop() 后任意指向头元素

c - 中断对外部模块/DLL 的所有调用