6

我已经阅读了各种描述,std::string::c_str包括多年来/几十年对 SO 提出的问题,

我喜欢这个描述的清晰:

返回一个指向数组的指针,该数组包含一个以空字符结尾的字符序列(即一个 C 字符串),表示字符串对象的当前值。此数组包含构成字符串对象值的相同字符序列,以及末尾的附加终止空字符 ('\0')。

然而,关于这个函数的目的的一些事情仍然不清楚。

您可能会认为调用可能会在存储在主机对象 ( ) 的内部 char 数组中的字符串末尾c_str添加一个字符,这是可以原谅的:\0std::string

s[s.size+1] = '\0'

但似乎std::string对象在调用之前默认为 Null 终止c_str在此处输入图像描述

看完定义后:

const _Elem *c_str() const _NOEXCEPT
{   // return pointer to null-terminated nonmutable array
    return (this->_Myptr());
}

我没有看到任何会添加\0到 char 数组末尾的代码。据我所知c_str,只是返回一个指向存储在数组第一个元素中的 char 的指针,就像这样begin()做一样。我什至没有看到检查内部数组是否被终止的代码\0

还是我错过了什么?

4

3 回答 3

9

在 C++11 之前,不需要 a std::string(或模板类 std::basic_string- 其中 std::string 是实例化)存储尾随'\0'. 这反映在 thedata()c_str()成员函数的不同规范中 -data()返回指向基础数据的指针(不需要以 a 终止'\0'c_str()返回带有 terminating 的副本'\0'。但是,同样,没有要求不存储尾随'\0'在内部(访问存储数据末尾之后的字符是未定义的行为)......并且,为简单起见,一些实现选择附加尾随'\0'

在 C++11 中,情况发生了变化。本质上,data()成员函数被指定为提供相同的效果c_str()(即返回的指针指向数组的第一个字符,该数组有一个尾随'\0')。其结果是需要在'\0' 返回的数组上加上尾随data(),因此在内部表示上也需要尾随。

因此,您看到的行为与 C++11 一致 - 类的不变量之一是尾随'\0' (即构造函数确保是这种情况,修改字符串的成员函数确保它保持真实,并且所有公共成员函数可以相信它是真的)。

您看到的行为与 C++11 之前的 C++ 标准并不矛盾。严格来说,std::string在 C++11 之前不需要维护尾随'\0',但同样,实现者可以选择这样做。

于 2017-01-05T13:27:25.480 回答
6

您看不到添加'\0'到序列末尾的代码,因为空字符已经存在。的实现c_str不能返回指向新数组的指针,因此数组必须存储在std::string对象本身上。

因此,您有两种有效的方法来实现这一点:

  1. 始终存储在构造字符数组'\0'的末尾,或_Myptr()
  2. 按需复制字符串,在调用'\0'时添加,并在析构函数中删除副本。c_str()

第一种方法让您返回_Myptr()for c_str(),代价是为每个字符串存储一个额外的字符。第二种方法需要每个std::string对象有一个额外的指针,因此第一种方法成本较低。

于 2017-01-05T13:07:22.590 回答
1

要求是c_str必须返回一个空终止的 cstring。没有什么说该函数必须添加空终止符。大多数实现(我认为所有想要符合标准的实现)都将空终止符存储在字符串本身使用的底层缓冲区中。原因之一是

std::string s;
assert(s[0] == '\0');

必须工作,因为 string 现在需要在string[string.size()]. 如果 string 没有将 null 终止符存储在底层缓冲区中,则[]必须进行边界检查以查看它是否在size()并且需要 return \0

于 2017-01-05T13:06:52.337 回答