【问题标题】:How const char* strings are compared?如何比较 const char* 字符串?
【发布时间】:2021-12-31 09:32:03
【问题描述】:

首先,考虑这个例子:

#include <iostream>
using namespace std;

int main()
{
    cout << ("123" == "123");
}

我期待什么:由于“123”是const char*,我希望比较这些字符串的地址(like one of these answers said)。

... 因为!=== 只会比较它们的基地址 字符串。不是字符串本身的内容。

但输出仍然是1。好吧,我们实际上不知道如何比较两个纯右值对象的地址(或者至少我不明白它是如何完成的)。所以让我们将这些字符串声明为变量,看看会发生什么:

#include <iostream>
using namespace std;

int main()
{
    const char* a = "1230";
    const char* b = "1230";
    cout << (a == b);
}

输出仍然是1。所以const char* 字符串不会衰减?或者编译器设法进行一些优化并只为一个字符串分配内存?好的,让我们尽量避免它们:

#include <iostream>
using namespace std;

int main()
{
    const char* a = "1230";
    const char* b = "1231";
    b = "1230";
    cout << (a == b);
}

结果还是一样。这让我觉得const char* 真的不会腐烂。但这并没有让我的生活变得更简单。那么const char*s 是如何比较的呢?

为什么这里的输出是1

#include <iostream>
using namespace std;

int main()
{
    const char* a = "1230";
    const char* b = "1231";
    cout << (a > b);
}

a 在词法比较方面小于b,但这里a 更大。那么const char*s的比较是如何实现的呢?

【问题讨论】:

  • a &gt; b 根本不进行字典比较。
  • 比较地址。您的编译器将字符串 once 存储在只读内存中,复制它没有意义,因此它总是返回 true。
  • 编译器对字符串文字进行重复数据删除。这就是为什么他们碰巧有相同的地址。
  • @tkausl - 你是对的,在这种情况下,编译器将字符串存储一次。这是一个实现细节,因此不一定总是正确的。相同的字符串文字可以分开存储。

标签: c++ pointers string-literals relational-operators


【解决方案1】:

是的,链接的答案是正确的。 operator== 指针只比较地址,而不是它们的内容。

此外,编译器是免费的,但不是必需的,可以重复字符串文字,因此所有出现的字符串文字都是相同的对象,具有相同的地址。这就是您所观察到的,重新分配 b = "1230"; 不会阻止这种情况。

[lex.string.14] 评估一个字符串文字会产生一个具有静态存储持续时间的字符串文字对象,从上面指定的给定字符初始化。是否所有字符串文字都是不同的(即,存储在不重叠的对象中)以及字符串文字的连续评估是否产生相同或不同的对象是未指定的。

const char* 应该衰减到什么位置?数组衰减,指针不会衰减。

#include <iostream>
using namespace std;

int main()
{
    const char* a = "1230";
    const char* b = "1231";
    cout << (a > b);
}

返回1 只是因为a 恰好指向比b 更高的地址,所以没有进行字典比较。如果需要,只需使用 std::stringstd::string_view

【讨论】:

    【解决方案2】:

    C++ 标准完全未指定文字字符串的存储细节(除了它们的生命周期),完全由编译器自行决定。例如:

    const char *a="ABCDEFG";
    const char *b="DEFG";
    

    智能编译器完全有可能只产生一个字符串,并将第二个指针设置为指向字符串的中间。

    也有可能来自不同.cpp文件的相同文字字符串在最终的、链接的可执行文件中只生成一个字符串,以及最初在不同.cpp完全编译的两个字符串结束up 具有相同的实际指针值。

    同样,指针比较也是为所有其他 C++ 标准中未明确指定的情况定义的实现。指针比较具有定义的行为,主要用于指向同一数组或向量的成员的指针,并且通常完全未指定其他方式。在 C++ 标准中,有一些方法可以实现指针的总顺序,但这与这里无关。

    总结一下:你不能期望任何指针值有任何特定的行为或特定的含义,否则。

    【讨论】:

      【解决方案3】:

      在这个比较中

      "123" == "123"
      

      const char[4] 类型的字符串字面量被隐式转换为指向它们的第一个元素的指针,并比较这些指针。

      结果取决于编译器选项,这些选项指定相同的字符串文字是存储为一个字符串文字还是单独的字符串文字。

      至于这个节目

      #include <iostream>
      using namespace std;
      
      int main()
      {
          const char* a = "1230";
          const char* b = "1231";
          cout << (a > b);
      }
      

      那么您不能将运算符 > 与不指向同一数组元素的指针一起使用。这样的比较是不确定的。

      比较的结果取决于编译器将字符串文字放入字符串文字池中的顺序。

      【讨论】:

        【解决方案4】:

        我希望对这些字符串的 ADDRESSES(就像其中一个答案所说的那样)进行比较。

        正确,这在 C 和 C++ 中都是如此。在 C 和 C++ 中比较 C 字符串(字符数组)或字符串文字时,编译器只比较它们的地址。

        或者编译器设法做了一些优化,只为一个字符串分配内存?

        是的!恰恰。编译器会看到"1230" 两次,可能(在您/我们的情况下,确实如此,这就是我们看到这种行为的原因)只需在相同的确切内存位置为两者使用相同的确切字符串它们在下面的代码中。因此,它们具有相同的地址。这是 C 和 C++ 编译器可能为您做的一个很好的优化。

        #include <iostream>
        using namespace std;
        
        int main()
        {
            const char* a = "1230";
            const char* b = "1230";
            cout << (a == b);
        }
        

        更进一步:

        为您进行优化的事实意味着即使在内存受限的嵌入式系统上,您也可以愉快地编写如下内容,因为您知道用完的程序空间不会每次都随着字符串文字的大小而增加您使用字符串文字:

        printf("some very long string\n");
        printf("some very long string\n");
        printf("some very long string\n");
        printf("some very long string\n");
        

        "some very long string" 在内存中只存储一次。

        话虽如此,如果您对该字符串进行了一个字符更改,编译器可能会选择使其成为内存中的新字符串,因此在上述情况下您最好无论如何都要这样做:

        constexpr char MY_MESSAGE[] = "some very long string\n";
        // OR:
        // #define MY_MESSAGE "some very long string\n"
        
        printf(MY_MESSAGE);
        printf(MY_MESSAGE);
        printf(MY_MESSAGE);
        printf(MY_MESSAGE);
        

        另见:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-05-03
          • 1970-01-01
          • 2013-03-23
          • 2013-01-03
          • 1970-01-01
          • 2011-07-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多