为什么(仅)某些编译器对相同的字符串文字使用相同的地址?


92

https://godbolt.org/z/cyBiWY

我可以'some'在MSVC生成的汇编代码中看到两个文字,但是只有一个带有clang和gcc。这导致代码执行的结果完全不同。

static const char *A = "some";
static const char *B = "some";

void f() {
    if (A == B) {
        throw "Hello, string merging!";
    }
}

谁能解释这些编译输出之间的区别和相似之处?为什么即使不要求优化,clang / gcc也会优化某些内容?这是某种不确定的行为吗?

我还注意到,如果将声明更改为以下所示,则clang / gcc / msvc根本不会"some"在汇编代码中保留任何声明。为什么行为不同?

static const char A[] = "some";
static const char B[] = "some";

4
stackoverflow.com/a/52424271/1133179对于一个密切相关的问题,有一些很好的相关答案,带有标准引号。
luk32 '18 -10-15


6
对于MSVC,/ GF编译器选项控制此行为。请参阅docs.microsoft.com/zh-cn/cpp/build/reference/…–
Sjoerd,

1
仅供参考,功能也会发生这种情况。
user541686 '18 -10-15

Answers:


109

这不是未定义的行为,而是未指定的行为。对于字符串文字

允许(但不是必需)编译器组合存储以相等或重叠的字符串文字。这意味着,当通过指针进行比较时,相同的字符串文字可能会比较相同,也可能会不同。

这意味着结果A == B可能是truefalse上,您不应该依赖。

根据标准[lex.string] / 16

是否所有字符串文字都是唯一的(即存储在不重叠的对象中)以及对字符串文字的连续求值是否得出相同或不同的对象,尚不确定。


36

其他答案解释了为什么不能期望指针地址不同。但是,您可以通过保证AB不会相等的方式轻松地重写此代码:

static const char A[] = "same";
static const char B[] = "same";// but different

void f() {
    if (A == B) {
        throw "Hello, string merging!";
    }
}

不同的是AB现在字符数组。这意味着它们不是指针,并且它们的地址必须与两个整数变量的地址必须相同。C ++混淆了这一点,因为它使指针和数组看起来可以互换(operator*并且operator[]看起来表现相同),但是它们实际上是不同的。例如,const char *A = "foo"; A++;完全合法,但const char A[] = "bar"; A++;不是这样。

考虑差别的一种方法是,char A[] = "..."说“给我的内存块,填充以字符...后跟\0”,而char *A= "..."表示“给我的,我可以查找的字符地址...,然后\0”。


8
如果您可以解释为什么会有所不同,这将是一个更好的答案。
Mark Ransom '18

请注意,*p并且p[0]不只是“似乎具有相同的行为”,但根据定义相同的(条件是p+0 == p为身份关系,因为0是在指针的整数加法中性元素)。毕竟p[i]定义为*(p+i)。答案虽然很好。
彼得-恢复莫妮卡

typeof(*p)并且typeof(p[0])char因此真的没剩下多少,可能是不同的。我确实同意“似乎表现相同”不是最好的措辞,因为语义是如此不同。您的帖子提醒我的最好办法C ++数组访问元素:0[p]1[p]2[p]等等。这是利弊如何做到这一点,至少当他们想混淆谁是C语言编程后出生的人。
tobi_s 18-10-16


这很有趣,我很想添加一个指向C FAQ的链接,但是我意识到有很多相关问题,但是似乎没有一个在这个问题上切入正题。
tobi_s

23

编译器是否选择使用相同的字符串位置AB取决于实现。正式地说,您可以说代码的行为是不确定的

两种选择都正确实现了C ++标准。


代码的行为是在第一次执行代码之前以未指定的方式抛出异常,或者什么也不选择。这并不意味着整体上的行为是不确定的-仅仅是编译器可以在首次观察到该行为之前以其认为合适的任何方式选择任何一种行为。
超级猫

3

这是节省空间的一种优化方法,通常称为“字符串池”。这是MSVC的文档:

https://msdn.microsoft.com/zh-CN/library/s0s0asdt.aspx

因此,如果将/ GF添加到命令行,则MSVC应该会出现相同的行为。

顺便说一句,您可能不应该通过这样的指针比较字符串,任何不错的静态分析工具都会将该代码标记为有缺陷。您需要比较它们指向的内容,而不是实际的指针值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.