char*c=";1234";。存储在c中的地址总是相同的

char*c=";1234";。存储在c中的地址总是相同的,c,pointers,C,Pointers,这是一位采访者提出的问题: #include<stdio.h> int main() { char *c="123456"; printf("%d\n",c); return 0; } #包括 int main() { char*c=“123456”; printf(“%d\n”,c); 返回0; } 这段代码总是打印一个固定的数字(例如13451392),无论执行多少次。为什么?您的代码包含未定义的行为:打印指针需要使用%p格式说明符,并且只有在将其转

这是一位采访者提出的问题:

#include<stdio.h>

int main()
{
    char *c="123456";
    printf("%d\n",c);
    return 0;
}
#包括
int main()
{
char*c=“123456”;
printf(“%d\n”,c);
返回0;
}

这段代码总是打印一个固定的数字(例如13451392),无论执行多少次。为什么?

您的代码包含未定义的行为:打印指针需要使用
%p
格式说明符,并且只有在将其转换为
void*
之后:

printf("%p\n", (void*)c);
这将产生一个依赖于系统的编号,在不同的平台上可能相同,也可能不同


它在您的平台上固定的原因可能是操作系统总是将您的可执行文件加载到虚拟内存的同一位置(虚拟内存可能映射到物理内存的不同区域,但您的程序永远不会知道)。字符串文字(作为可执行文件的一部分)也将在同一位置结束,因此打印输出将始终相同。

回答您的问题,字符串“123456”是内存中的一个静态常量,加载.exe时,它总是进入相同的内存位置

c
是(或者更确切地说是它包含的内容)字符串的内存地址,正如我所说的,它总是在同一个位置。如果您将地址打印为十进制数字,则可以看到地址的十进制形式


当然,正如@dasblinkenlight所说,您应该将其作为指针打印,因为不同的机器/语言对于指针的大小和整数的大小有不同的约定。

大多数可执行文件格式都有一个选项,可以告诉操作系统加载程序在哪个位置加载可执行文件,例如,Windows使用的字段用于此字段,对于应用程序,通常设置为
0x00400000

当加载器第一次加载可执行文件时,它会尝试在该地址加载它,如果没有使用它,它会在该地址加载它,这基本上是正确的,但是如果使用了它。它在系统给定的不同地址加载

这里的情况是,数据部分中到您的
“12345”
的偏移量是相同的,并且操作系统以相同的基址加载映像基址,因此您总是得到相同的虚拟地址,即base+offset

但情况并非总是如此,出于上述原因,可以使用基址,许多使用MSVC编译的Windows DLL将其基址设置为
0x10000000
,因此在该地址仅加载一个或一个

另一种情况是,当存在地址空间随机化、安全功能时,如果系统支持并启用该功能,则MSVC具有链接器选项,系统将忽略指定的映像库,并自行为您提供不同的随机地址

要总结两件事:

  • 您不应该依赖于这种行为,系统可以在任何地址加载您的程序,因此您将给出不同的地址
  • 使用
    %p
    打印地址,在某些系统上,例如,
    int
    为4字节,指针为8字节,部分地址将被截断

问题是“它为什么会改变?”使用
%p
打印指针。提示:在支持此功能的系统上(当然是Linux,我认为OSX)使用
gcc-pie-fpie
编译,每次执行时打印的值都会改变。给你。想想这意味着什么。你的标题问的是“c的地址”。您的程序打印
c
的值,该值恰好是指针(即地址)。并显示打印该值的正确方式。指针对象
c
的地址也可能相同(但决不能确定);您可以使用printf(&c=%p\n),(void*)&c)来找出。@Zack:指出一个错误并不是“固定”在它上面——而且它可能对OP来说并不明显。(好的编辑,顺便说一句)大小不是唯一的问题。即使
sizeof(int)==sizeof(int*)
,使用
%d”
打印指针值仍有未定义的行为。(例如,指针和整数可能使用不同的机制作为参数传递。)@Keith:是的,我注意到了,谢谢。是的,我知道这种行为是不确定的,但对于一个新手,我不想通过吹毛求疵来混淆他。我不想鼓励新手认为,如果你碰巧知道他们的大小相同,那么使用
“%d”
打印指针值是可以的。而且
“%p”
将以更合理的格式打印地址(通常,但不总是十六进制)。@Keith:从我的教学生涯中,我总是发现编程的基本思想更容易理解,如果人们可以将指针视为包含地址的变量,地址只不过是一个数字,这恰好意味着某些东西在记忆中的位置。一旦它们有了具体的基础,我们就可以抽象地说指针是一个与数字不同的东西。如果我一开始就这么说的话,会让人困惑。他们想知道它是什么,而不是它不是什么。啊,但是指针不是数字,尽管它们共享一些属性。我看到太多的C程序员从未忘记指针只是数字的想法。(例如,添加两个指针毫无意义。)《加速C++》一书对此采取了一种有趣的方法。它很早就介绍了容器和迭代器,并展示了如何使用它们。然后,在后面的一章中,介绍了数组作为一种低级容器,指针作为一种低级迭代器。显然,这并不直接适用于C,但我喜欢这个想法。