是否保证相同内容字符串文字的存储相同?

Is storage for the same content string literals guaranteed to be the same?

下面的代码安全吗?编写类似于此的代码可能很诱人:

#include <map>

const std::map<const char*, int> m = {
    {"text1", 1},
    {"text2", 2}
};

int main () {
    volatile const auto a = m.at("text1");
    return 0;
}

该映射仅用于字符串文字。

我认为这是完全合法的,而且似乎有效,但是我从来没有看到保证在两个不同的地方使用的文字指针是相同的。我无法让编译器为具有相同内容的文字生成两个单独的指针,所以我开始怀疑这个假设有多稳固。

我只对内容相同的字面量是否可以有不同的指针感兴趣。或者更正式地说,上面的代码可以除外吗?

我知道有一种方法可以编写代码以确保其有效,而且我认为上述方法很危险,因为编译器可能会决定为文字分配两个不同的存储空间,尤其是当它们被放置在不同的翻译单元中时。我说得对吗?

C++ 标准不要求实现删除重复字符串文字。

当字符串文字驻留在另一个翻译单元或另一个需要链接器 (ld) 或运行时链接器 (ld.so) 执行字符串文字去重的共享库中时。他们没有。

不,C++ 标准不提供此类保证。

也就是说,如果代码在同一个翻译单元中,那么很难找到反例。如果 main() 是不同的翻译,那么反例可能更容易产生。

如果映射在不同的动态链接库或共享对象中,那么几乎可以肯定不是这种情况。

volatile 限定符是一条红鲱鱼。

具有完全相同内容的两个字符串文字是否是完全相同的对象,未指定,在我看来最好不要依赖。引用标准:

[lex.string]

16 Evaluating a string-literal results in a string literal object with static storage duration, initialized from the given characters as specified above. 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.

如果您希望避免 std::string 的开销,您可以编写一个简单的视图类型(或使用 C++17 中的 std::string_view),它是字符串文字上的引用类型。使用它进行智能比较,而不是依赖文字标识。

标准不保证具有相同内容的字符串文字的地址相同。事实上,[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.

第二部分甚至说,当第二次调用包含字符串文字的函数时,您可能不会获得相同的地址!虽然我从未见过编译器这样做。

因此,当字符串文字重复时使用相同的字符数组对象是一种可选的编译器优化。通过安装 g++ 和默认编译器标志,我还发现我在同一个翻译单元中为两个相同的字符串文字获得了相同的地址。但是如你所料,如果相同的字符串文字内容出现在不同的翻译单元中,我会得到不同的结果。


一个相关的有趣点:不同的字符串文字也允许使用重叠数组。即给定

const char* abcdef = "abcdef";
const char* def = "def";
const char* def0gh = "def[=10=]gh";

您可能会发现 abcdef+3defdef0gh 都是相同的指针。

此外,这条关于重复使用或重叠字符串文字对象的规则仅适用于与文字直接关联的未命名数组对象,如果文字立即衰减为指针或绑定到对数组的引用,则使用。文字也可用于初始化命名数组,如

const char a1[] = "XYZ";
const char a2[] = "XYZ";
const char a3[] = "Z";

此处数组对象 a1a2a3 使用字面量进行初始化,但被认为与实际字面量存储不同(如果这种存储甚至存在)并遵循普通对象规则,因此这些数组的存储不会重叠。