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

时间:2018-03-06 16:33:58

标签: c string pointers memory

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

#include <stdio.h>
#include <stdlib.h>

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如何才能拥有相同的地址?它们可能包含相同的字符串,但它们不是同一个变量。

7 个答案:

答案 0 :(得分:23)

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

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

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

答案 1 :(得分:14)

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

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

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

我已经从litteral字符串声明了3个不同的char数组 initialized 。测试它,你会看到编译器为3个不同的字符串分配了不同的地址。

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

答案 2 :(得分:9)

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

描述字符串文字的C standard的第6.4.5节说明了以下内容:

  

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

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

  

使用未指定的值或其他行为   国际标准提供两种或更多种可能性和强加   在任何情况下都没有选择进一步的要求

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

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

答案 3 :(得分:3)

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

答案 4 :(得分:2)

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

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

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

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

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

答案 5 :(得分:1)

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

答案 6 :(得分:-2)

这是因为每个硬编码的字符串都像&#34; First&#34;和&#34;第二&#34;存在于&#34;只读&#34;部分可执行文件,因此它们有一个地址。

在linux上,您可以使用&#34; objdump -s -j .rodata execfile&#34;来查看它们。

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