C++中的字符串字面量相等性检查?

4

==不是我们比较两个数组的方式,因为这只会比较地址:

#include <iostream>

int main()
{
  char a[] = "aaa";
  char b[] = "aaa";

  if (a == b)
    std::cout << "Yes" << std::endl;
  else
    std::cout << "No" << std::endl;

  return 0;
}

这段代码甚至给了我一个警告:

数组比较始终会评估为false

但是当我尝试了以下代码时:

if ("aaa" == "aaa")

看起来它运行得很好。仍然会给我一个警告,但是这个警告是:

条件总是为真

起初,我以为这是一些缓存问题,所以我尝试了一个相当不寻常的字符串字面量:

if ("whuiwhqohqweihqweohi" == "whuiwhqohqweihqweohi")

在MSVC和g++上都能正常工作。这是一种实现相关的行为吗?我知道比较编译时已知的变量并不是很有用,但我的问题只是“为什么会这样?”

此外,使用auto似乎也可以正常工作:

#include <iostream>

int main()
{
  auto a = "whuiwhqohqweihqweohi";
  auto b = "whuiwhqohqweihqweohi";
  if (a == b) {
    std::cout << "Yes" << std::endl;
  }
  else {
    std::cout << "No" << std::endl;
  }

  return 0;
}

这段代码输出正确。这里的ab的类型是什么?请不要回答“使用std::string”。这与我的问题无关。

3
编译器允许但不强制将相等或重叠的字符串字面量合并为一个存储块。这意味着相同的字符串字面量在通过指针比较时可能相等,也可能不相等。 - KamilCuk
@KamilCuk 这回答了我的问题。 - Ayxan Haqverdili
3个回答

7

在这里你必须非常小心,因为你正在查看的一些情况并不完全等同于其他情况。

以您的第一个示例为例:

char a[] = "aaa";
char b[] = "aaa";

if (a == b)

您正在创建两个char数组,每个数组都从字符串文字初始化。然后,您尝试将这些数组相互比较。在大多数情况下(包括此示例),数组的名称评估为该数组中第一个元素的地址。因此,您实际上正在比较两个数组的地址。这些地址不能相同,因此比较保证会产生false。
在您的第二个示例中:if("aaa" == "aaa"),您正在比较字符串文字本身,而不是从字符串文字初始化的数组。
标准不保证此结果。标准允许(但不要求)将相同的字符串文字合并在一起。但是,您实际上正在比较的不是字面量的内容-而是它们存储的地址。如果编译器将字符串文字合并以使它们位于同一地址,则会产生true。如果将它们保持分开,则会产生false。
在您的auto案例中:
auto a = "whuiwhqohqweihqweohi";
auto b = "whuiwhqohqweihqweohi";

您的情况与之前相同——ab都最终成为指向char的指针,保存字符串字面值的地址。如果编译器合并这些字面值,它们将指向相同的地址,因此它们将比较相等。如果编译器没有将它们合并在一起,每个字符串都有自己的地址,它们将比较不相等。
重点是这些比较都没有比较字符串的内容,只比较它们存储的地址。内容只有在两个字符串字面值具有(或至少以)相同的内容时才能被合并。
至于“至少以”是指如果您在一个地方写了"wing",在另一个地方写了"swing",编译器可以自由地合并它们,所以代码类似于:
auto a = "wing";
auto b = "swing";

编译器可以将swing存储在一个位置,并将a初始化为指向该存储文字面量的第二个字符。


"wing"/"swing" 部分尤其有趣。 当我有两个相同的 const int [] 时,是否也是这种情况? - Ayxan Haqverdili
不可以,数组不能有相同的地址。 - Ted Lyngmo
@TedLyngmo 即使它们是 constexpr,也有这种限制?为什么会有这种限制? - Ayxan Haqverdili
@Ayxan:根据“好像”规则,编译器可能会合并它们,前提是您比较它们的地址。但是,如果您比较它们的地址,并发现它们相等,则可以检测到优化,这将不再符合“好像”规则。 - Jerry Coffin
@Ayxan 如果您创建两个 const int*,则可以获得与 const char* 相同的结果。但是我无法想到一种好的方法来创建 int 字面量列表。不过,可以通过字符串字面量进行转换:int const* a = reinterpret_cast<int const*>("...."); 但这并不能真正显示出 const int* 是否被不同对待,因为编译器可能会首先合并 const char* 存储。 - Ted Lyngmo

6

对于

char a[] = "aaa";
char b[] = "aaa";

你比较本地数组ab的地址,所以不能有相同的地址。
对于
if ("aaa" == "aaa")

你比较两个静态字符指针,它们可能相同也可能不同。
来自string_literal

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.

bool b = "bar" == 3+"foobar" // could be true or false, implementation-defined
在同一情况下:
auto a = "whuiwhqohqweihqweohi";
auto b = "whuiwhqohqweihqweohi";

autoconst char*


“which might be identical or not”是某些实现特定的内容吗?考虑到“条件始终为真”的警告。 - Ayxan Haqverdili
1
@Ayxan:这是实现特定的,所以对于你的实现来说,它总是正确的。 - Jarod42

3

这两个字面字符串是常量,编译器很可能会将您代码中的这两个实例替换为一个实例,然后替换为 true。

auto ab 都是 char const* 类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接