【问题标题】:Why are string literals const?为什么字符串文字是 const?
【发布时间】:2013-01-12 07:41:12
【问题描述】:

众所周知,在 C++ 中字符串文字是不可变的,修改字符串文字的结果是未定义的。例如

char * str = "Hello!";
str[1] = 'a';

这将导致未定义的行为。

此外,字符串文字被放置在静态内存中。所以它们存在于整个程序中。我想知道为什么字符串文字有这样的属性。

【问题讨论】:

  • 它并不总是运行时错误,它是未定义的行为。这意味着它可以做任何事情
  • 因为根据当前的链接描述文件,它们可能被放置在 ROM 中。
  • 这种行为在 C++03 中被弃用,在 C++11 中被禁止。
  • @ipc 我试过用Visual Studio 2012(使用新的C++标准)编写这段代码,但仍然存在错误。
  • 我不知道 Visual Studio,但如果它们完全符合 C++11,则此代码不应编译。

标签: c++ string constants literals


【解决方案1】:

修改文字是未定义的行为,因为标准是这样说的。标准这样说是为了允许编译器将文字放入只读内存中。它这样做有很多原因。其中之一是允许编译器进行优化,即只存储在源代码中重复多次的文字的一个实例。

【讨论】:

    【解决方案2】:

    因为是 K&R C,所以没有“const”这样的东西。在 ANSI C++ 之前也是如此。因此,有很多代码包含char * str = "Hello!"; 之类的内容,如果标准委员会将文本文字设为 const,那么所有这些程序都将不再编译。所以他们做出了妥协。文本字面量是官方的 const char[],但它们具有到 char* 的静默隐式转换。

    【讨论】:

    • C++11 想和你谈谈。
    【解决方案3】:

    有几个不同的原因。

    一种是允许将字符串文字存储在只读内存中(正如其他人已经提到的那样)。

    另一个是允许字符串文字的合并。如果一个程序在几个不同的地方使用相同的字符串字面量,最好允许(但不一定要求)编译器将它们合并,这样您就可以获得指向同一内存的多个指针,而不是每个指针占用一个单独的内存块。这也适用于两个字符串文字不一定相同,但确实有相同的结尾:

    char *foo = "long string";
    char *bar = "string";
    

    在这种情况下,bar 可能是 foo+5(如果我计算正确的话)。

    在任何一种情况下,如果您允许修改字符串文字,它可能会修改恰好具有相同内容的 other 字符串文字。同时,老实说,强制这样做也没有什么意义——拥有足够多的字符串文字可以重叠,大多数人可能希望编译器运行得更慢只是为了节省(也许)几十个字节,这是非常罕见的大约内存。

    在编写第一个标准时,已经有编译器使用了所有这三种技术(可能还有一些其他技术)。由于无法描述修改字符串文字所产生的一种行为,而且显然没有人认为这是一种重要的支持能力,因此他们做了显而易见的事情:说即使尝试这样做也会导致未定义的行为。

    【讨论】:

      【解决方案4】:

      我相信您会问为什么将文字放在 只读存储器,而不是关于链接器执行此操作的技术细节和 该或禁止某某标准的法律细节。

      当修改字符串文字工作时,它会导致一些细微的错误 即使没有字符串合并(我们有理由 如果我们决定允许修改,则不允许)。当你看到类似的代码时

      char *str="Hello";
      .../* some code, but str and str[...] are not modified */
      printf("%s world\n", str);
      

      很自然地得出结论,您知道将要打印什么, 因为str(及其内容)没有在特定的 位置,介于初始化和使用之间。

      然而,如果字符串字面量是可写的,你不知道任何 more: str[0] 可以在以后被覆盖,在这个代码中或者在一个 深度嵌套的函数调用,以及再次运行代码时

      char *str="Hello";
      

      不再保证str 的内容。正如我们 期望,这个初始化被实现为移动已知的地址 在链接时间进入str 的位置。它不检查str 包含“Hello”并且它不分配它的新副本。然而, 我们理解此代码将str 重置为“Hello”。很难 克服这种自然的理解,很难推理 无法保证的代码。当你看到类似的表达 x+14,如果您不得不考虑可能会覆盖 14 怎么办 在其他代码中,所以它变成了 42?字符串也有同样的问题。

      这就是不允许修改字符串文字的原因,无论是在 标准(不要求及早发现故障)和 实际目标平台(提供检测潜力的奖励 错误)。

      我相信许多解释这件事的尝试都受到了 最糟糕的循环推理。该标准禁止写入 文字,因为编译器可以合并字符串,或者可以将它们放在 在只读存储器中。它们被放置在只读存储器中以捕获 违反标准。合并文字是有效的,因为 标准禁止...这是您要求的一种解释吗?

      让我们看看其他 语言。 Common Lisp standard 使文字的修改未定义的行为,即使 之前 Lisp 的历史与 C 的历史非常不同 实施。这是因为可写字面量是逻辑上的 危险的。语言标准和内存布局仅反映了这一点 事实。

      Python 语言只有一个地方有类似的东西 “写入文字”可能会发生:参数默认值,而这 事实confuses people all the time

      你的问题被标记为C++,我不确定它的当前状态 关于隐式转换到非常量char*:如果它是 转换,是否已弃用?我希望其他答案能够提供 在这一点上完全启蒙。当我们谈论其他语言时 在这里,让我提一下普通 C。这里,字符串文字是 not const, 并且要问的一个等效问题是 为什么我不能修改字符串 文字(而有更多经验的人会问,为什么 如果我不能修改它们,字符串文字非常量?)。然而 尽管存在这种差异,上述推理完全适用于 C。

      【讨论】:

      • 从什么时候开始,C++ 开始关心防止人们在脚上开枪了?另外,为什么原始数组不存在同样的问题:int arr[] = {1, 2, 3};
      【解决方案5】:

      在 C++ 中,字符串字面量为 const,因为不允许使用 修改它们。在标准 C 中,它们应该是 const 好吧,除了 const 被引入 C 时,还有 char* p = "somethin"; 这样的代码太多了 使它们成为 const 会破坏,它被认为是 不可接受。 (C++ 委员会选择了不同的解决方案 这个问题,不推荐使用的隐式转换允许 以上。)

      在最初的 C 中,字符串文字是 not const,并且是 可变的,并且保证没有两个字符串文字共享 任何记忆。很快就意识到这是一个严重的错误, 允许这样的事情:

      void
      mutate(char* p)
      {
          static char c = 'a';
          *p = a ++;
      }
      

      在另一个模块中:

      mutate( "hello" );  //  Can't trust what is written, can you.
      

      (Fortran 的一些早期实现也有类似的问题, 其中F(4) 可能会以几乎任何整数值调用F。 Fortran 委员会修复了这个问题,就像 C 委员会一样 修复了 C 中的字符串文字。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-05
        • 2020-09-25
        • 2017-10-22
        • 2011-05-20
        • 1970-01-01
        • 2021-05-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多