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

Why do (only) some compilers use the same address for identical string literals?

https://godbolt.org/z/cyBiWY

我可以在 MSVC 生成的汇编代码中看到两个 'some' 文字,但只有一个带有 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";

这不是未定义的行为,而是未指定的行为。对于 string literals

The compiler is allowed, but not required, to combine storage for equal or overlapping string literals. That means that identical string literals may or may not compare equal when compared by pointer.

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

根据标准,[lex.string]/16

Whether all string literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified.

编译器是否选择对 AB 使用相同的字符串位置取决于实现。形式上你可以说你的代码的行为是 unspecified.

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

其他答案解释了为什么您不能指望指针地址不同。然而,您可以轻松地以保证 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[] = "..." 表示 "give me a block of memory and fill it with the characters ... followed by [=21=]",而 char *A= "..." 表示 "give me an address at which I can find the characters ... followed by [=21=]"。

是保存space的优化,通常叫"string pooling"。这是 MSVC 的文档:

https://msdn.microsoft.com/en-us/library/s0s0asdt.aspx

因此,如果您将 /GF 添加到命令行,您应该会看到与 MSVC 相同的行为。

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