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

Jos*_*son -2 c pointers casting

任何人都可以向我解释在下面的代码片段的运行时期间发生了什么,为什么它打印33?谢谢,

#include <stdio.h>

void main(){
    int* p = (int*)17;
    printf("%d\n", (int)(long)(p+4));
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*mit 6

p是指向某个int或某个ints 的指针.

你初始化它指向地址17.(这是一个很大的问题,有几个原因,我们会得到.)

然后你添加4.显然,sizeof(int)在你的机器上是4,即int占用4个字节(32位).当你向int指针添加4时,编译器知道你想让它int更值得点4 ,所以编译器将4×4 = 16加到地址上.现在p指向地址33.

然后你p从指针"转换"(转换)到long.所以现在,而不是指向地址33的指针,它只是数字33.

然后,你再投了,从longint.这基本上将其转换从33至33(如果类型long在机器上是大于4个字节大,此转换可能已涉及,比如说,从64位值33转换为32位值33,即,从0x00000000000000210x00000021.)

然后使用,打印出最后一个int%d.所以你看到33号.

现在,通常,说一些像

int* p = (int*)17;
Run Code Online (Sandbox Code Playgroud)

是一个坏主意,因为你有一个指针,其值可能无法使用.通常,使用指针执行的一件事是操纵它们指向的值.但如果你要说

printf("value pointed to by p is %d\n", *p);
Run Code Online (Sandbox Code Playgroud)

你最终会尝试int从内存中的地址17 获取一个值.但是(a)你可能没有从地址17读取的权限,而(b)17不是4的倍数,所以int即使你有权限,你的处理器甚至可能不愿意尝试从那里取一个.所以这段代码肯定会崩溃.

但是因为在你的代码中,你实际上从来没有尝试用int假设指向的任何东西p(既不是在它之前也不是在它之后),你的代码可能 - 而且几乎没有 - 似乎"工作".

一方面,如果不是完全未定义的代码,这是不好的,不可移植的,几乎没有定义.但话说回来,它可能并不打算实用(显然没有人会用它来完成任何实际的工作).因此,如果我们所有的内容都是指针运算如何工作的一个教训(特别是它如何根据指向对象的大小由编译器自动缩放),也许我们不必花太多时间贬低它的许多丑闻和不完美之处.(关于代码是未定义还是仅仅是实现定义,在注释线程中有一点点狡辩,但只要你注意不要真正编写这样的代码,你不一定要担心区别.)

如果你想学习更多或更少关于指针算术的课程,但是使用更合理且最便携的程序,试试这个:

#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));
}
Run Code Online (Sandbox Code Playgroud)

在这里,p我们将初始化它指向一个实际的数组,而不是像17那样填充一个人工值.而不是将指针值转换为int并使用它打印%d,我们使用打印它来%p设计打印指针.

在我的机器上,这个程序打印出来

p is 0x10f47a02c and points to 3
p+4 is 0x10f47a03c and points to 7
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,指针值不容易消化,如17和33这样的小数字,我的机器选择以十六进制打印它们.然而,很容易验证0x10f47a03c- 0x10f47a02c0x10,或16.我们添加了4,意思是" int让它指向现在指向的第4个过去",编译器增加了16个.

[脚注.我说这是"大多数便携式".为了使其完全便携,您必须将printf呼叫更改为

    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));
Run Code Online (Sandbox Code Playgroud)

严格来说,%p只定义打印通用指针void,而不是任意其他指针类型.所以,严格来说,我们需要那些(void *)强制转换,将指针值转换为正确的指针类型进行打印.

  • @FelixPalmen:C 2011 6.5.6 8用指针定义加法.它指定某些情况的结果,这些情况都不适用于此处,然后说"......否则,行为未定义". (2认同)
  • @FelixPalmen仅在同一数组中的指针上显式定义指针算法的原因是承认两个不同的数组可能位于两个不同的内存段中的可能性.我称之为支持非平面地址空间!如果`p`和`q`指向不同的数组,计算'pq`与询问"123 Main St.和456 First Ave.之间有多少房子"一样有意义?(答案可能是*不是*蝇蝇或毕达哥拉斯的距离,更不用说"曼哈顿距离"了:-).) (2认同)