不同的字符串如何具有相同的地址

我知道为了比较C中的两个字符串,你需要使用strcmp()函数。 但我试着用==运算符比较两个字符串,它有效。 我不知道如何,因为它只是比较两个字符串的地址。 如果字符串不同,它应该不起作用。 但后来我打印了字符串的地址:

 #include  #include  int main() { char* str1 = "First"; char* str2 = "Second"; char* str3 = "First"; printf("%p %p %p", str1, str2, str3); return 0; } 

输出是:

 00403024 0040302A 00403024 Process returned 0 (0x0) execution time : 0.109 s Press any key to continue. 

str1str3有多少可能具有相同的地址? 它们可能包含相同的字符串,但它们不是同一个变量。

无法保证它永远都是这样的。 通常,实现者维护一个文字池,只保留每个字符串文字一次,然后对于字符串文字的多个用法,使用相同的地址。 但是人们可能会以不同的方式实现它 – 标准不会对此构成约束。

现在你的问题是:你正在查看指向同一个字符串文字的两个指针的内容。 相同的字符串文字产生相同的值(它们衰变为指向第一个元素的指针)。 但由于第一段中陈述的原因,该地址是相同的。

另外,我要强调使用(void*)强制转换提供%p格式说明符的参数。

这里有一个有趣的观点。 你实际上只有3个指针指向const litteral字符串。 因此编译器可以自由地为"First"创建一个单独的字符串,并且str1str3指向那里。

这将是一个完全不同的情况:

 char str1[] = "First"; char str2[] = "Second"; char str3[] = "First"; 

我声明了3个不同的char数组,这些数组是从litteral字符串初始化的 。 测试它,您将看到编译器为3个不同的字符串分配了不同的地址。

你应该记住的是:指针和数组是不同的动物,即使数组可以衰减到指针(在这篇文章中更多关于它的C FAQ )

当特定字符串文字在源文件中多次出现时,编译器可以选择将该文字的所有实例指向同一位置。

C标准的 6.4.5节描述了字符串文字,其中说明如下:

7如果这些数组的元素具有适当的值,则这些数组是否不同是未指定的。 如果程序试图修改此类数组,则行为未定义。

其中“未指明的行为”在第3.4.4节中定义为:

使用未指明的值,或本国际标准提供两种或更多种可能性的其他行为,并且在任何情况下都不会对其进行任何进一步的要求

在您的情况下,字符串文字"First"在源中出现两次。 因此编译器对两者使用相同的文字实例,导致str1str3指向同一个实例。

如上所述,不保证这种行为。 "First"的两个实例可以彼此不同,导致str1str3指向不同的位置。 字符串文字的两个相同实例是否位于同一位置是未指定的。

字符串文字,就像C99 +复合文字一样,可以合并。 这意味着源代码中的两个不同事件实际上可能导致正在运行的程序中只有一个实例。
如果您的目标不支持硬件写保护,情况甚至可能就是这种情况。

这是如此令人困惑的原因可能是,“但如果我设置str1[1] = 'u';会发生什么str1[1] = 'u'; ?“因为它的实现定义了str1 == str3 (以及文字"world!"的地址是否是"hello, world!"的地址加上7),那aldo会把str3变成德国王子吗?

答案是:也许吧。 或者它可能只更改str1 ,或者它可能无声地更改,或者它可能因为您写入只读内存而崩溃程序,或者它可能会导致其他一些微妙的错误,因为它将这些字节重新用于另一个目的或完全不同的东西。

您甚至可以将字符串文字分配给char*而不需要使用const char*这一事实基本上是为了几十年前的遗留代码。 C的第一个版本没有const 。 一些现有的编译器让程序改变字符串常量,而有些则没有。 当标准委员会决定将const关键字从C ++添加到C时,他们不愿意破坏所有代码,因此当程序更改字符串文字时,他们允许编译器基本上做任何事情。

这样做的实际意义是: 永远不要将字符串文字分配给不是constchar* 。 并且永远不要假设字符串常量重叠或不重叠(除非您使用restrict保证这一点)。 自1989年以来,这种类型的代码已经过时,只是让你自己射击。 如果你想要一个指向字符串文字的指针(可能会或可能不会与其他常量共享内存),请将它存储在const char*或更好的const char* const 。 如果您尝试修改它,则会发出警告。 如果您想要一个可以修改的char数组(并且保证不会为任何其他变量设置别名),请将其存储在char[]

如果您认为要根据地址比较字符串,那么您真正想要的是哈希值或唯一句柄。

要添加其他答案:这是一种称为字符串实习的技术,其中编译器意识到字符串是相同的,因此只存储一次。 Java也倾向于这样做(但是,正如另一张海报所提到的,它依赖于编译器)。

这是因为每个硬编码的字符串如“First”和“Second”都出现在可执行文件的“只读”部分中,因此它们有一个地址。

在linux上,你可以使用“objdump -s -j .rodata execfile”来查看它们。

如果您尝试显示str1,str2和str3地址,您将看到有不同的地址。