【问题标题】:Unexpected behavior on adding '\0' to std::string将 '\0' 添加到 std::string 时出现意外行为
【发布时间】:2013-07-01 11:09:58
【问题描述】:

为什么 C++ 标准允许以下内容?

#include <iostream>
#include <string>    

int main()
{
    std::string s(10, '\0'); // s.length() now is 10
    std::cout << "string is " << s << ", length is " << s.length() << std::endl;
    s.append(5, '\0'); // s.length() now is 15 
    std::cout << "string is " << s << ", length is " << s.length() << std::endl;
    // the same with += char and push_back 

    // but:
    s += "hello"; // s.length() returns 20 string is "hello"
    std::cout << "string is " << s << ", length is " << s.length() << std::endl;

    return 0;
}

为什么要加0并计数呢? 看起来字符串的完整性被破坏了,不是吗?但我检查了标准,这是正确的行为。

【问题讨论】:

  • std::string 不是以 null 结尾的字符串。
  • “字符串完整性受损”? std::string 的全部意义在于,您不受 1970 年以来的任意限制,例如“不能包含 \0”。
  • 如果您检查了标准并发现这是正确的行为,您的问题是什么?
  • std::string 视为一种字符容器,其中包含一堆字符串功能。
  • += "hello" 行之后,该字符串不是 5 个字符的字符串 "hello",正如您的评论所暗示的那样。该字符串实际上是"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0hello",但您的终端可能无法打印 ASCII NUL 字符,因此它似乎只打印"hello"。但是,如果您将程序的输出保存到一个文件并检查原始文件数据,您会在其中看到 15 个 NUL 字符。

标签: c++ string stl


【解决方案1】:

为什么标准允许跟随?

因为设计 C++ 字符串的人决定应该允许这样的事情。我不确定设计 C++ 字符串的团队中是否有人在 SO 上……但既然你自己说标准允许它,那就是它的方式,我怀疑它即将改变。

有一个可以包含“任何东西”的字符串有时是很实用的。当我不得不解决 C 样式字符串不能包含零字节这一事实时,我可以想到一些实例。除了长 C 风格的字符串需要很长时间才能找到长度这一事实外,C++ 字符串的主要好处是它们不限于“你可以放入其中的内容”——这在我的书中是一件好事。

【讨论】:

  • c_str() 确实返回了一个空终止字符串,这意味着 std::string 实现必须同时使用长度字段 一个空终止符。 .
  • @Roddy:是的,如果你在中间填一个零,你显然不能以传统的 C 风格字符串方式将它用作 c_str() 字符串。你不能吃蛋糕。实际上,直到你称之为c_str() 的这一点,我不相信实现必须在最后填充一个零。但我怀疑大多数实现都会这样做,仅仅是因为这样做比重新分配字符串并在你点击 c_str() 时在其中填充一个零更容易 - 特别是在 C++ 11 中,c_str() 被定义为从字符串,而不是辅助副本。
【解决方案2】:

不知道这里有什么问题。

std::string 中间添加'\0' 不会改变任何内容 - 空字符被视为与任何其他字符一样。唯一可以改变的是,如果您将.c_str() 与接受空终止字符串的函数一起使用。但这不是.c_str()的问题,只有对'\0'有特殊处理的功能。

如果您想知道这个字符串有多少个字符被视为以空字符结尾的字符串,请使用

size_t len = strlen(s.c_str());

请注意,这是 O(n) 操作,因为这就是 strlen 的工作方式。

如果您问为什么 += 运算符不将字符串文字 "hello" 的隐式空字符添加到字符串中,我说相反(添加它)并不清楚,而且绝对不是您想要的 99% .另一方面,如果您想将'\0' 添加到您的字符串中,只需像缓冲区一样附加它:

char buffer[] = "Hello";
s.append(buffer, sizeof(buffer));

或者(甚至更好)完全删除 char 数组和以 null 结尾的字符串,并使用 C++ 样式的替换,例如 std::string 作为 NTS 替换,std::vector&lt;char&gt; 作为连续缓冲区,std::vector 作为带有指针替换的动态数组,和 std::array (C++11) 作为标准 C 数组替换。

另外,(正如 @AdamRosenfield 在 cmets 中提到的那样),添加 "hello" 后的字符串实际上确实有 20 个字符,可能只是您的终端不打印空值。

【讨论】:

    【解决方案3】:

    NUL char '\0'c style string 的结束字符,而不是std::strings。但是,它支持此字符从 const char 指针获取值,以便它可以找到 c 样式字符串的结尾。否则,它就像其他字符一样处理

    【讨论】:

      【解决方案4】:

      std::string 更像是一个字符容器,而不是其他任何东西,而 \0 是一个字符。作为一个真实的例子,看看 Windows 中的 CreateProcess 函数。 lpEnvironment 参数采用以空字符结尾的字符串块(即A=1\0B=2\0C=3\0\0)。如果你正在构建一个块,使用 std::string 会很方便。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-15
        • 1970-01-01
        相关资源
        最近更新 更多