我已经阅读了std::string::c_str
的各种描述,包括多年来/几十年来提出的问题,
我喜欢这个描述的清晰度:
返回指向包含以null结尾的序列的数组的指针 表示当前值的字符(即,C字符串) 字符串对象。该数组包含相同的字符序列 构成字符串对象的值加上一个额外的 最后终止空字符(' \ 0')。
然而,有关此功能目的的一些事情仍不清楚。
您可能原谅认为调用c_str
可能会在字符串末尾添加\0
字符,该字符串存储在主机对象的内部char数组中(std::string
) :
s[s.size+1] = '\0'
但是,即使在调用std::string
之前,默认情况下c_str
对象仍然是Null终止:
查看定义后:
const _Elem *c_str() const _NOEXCEPT
{ // return pointer to null-terminated nonmutable array
return (this->_Myptr());
}
我没有看到任何将\0
添加到char数组末尾的代码。据我所知c_str
只返回一个指向存储在数组第一个元素中的字符的指针,就像begin()
一样。我甚至看不到代码检查内部数组是否被\0
或者我错过了什么?
答案 0 :(得分:6)
您没有看到将'\0'
添加到序列末尾的代码,因为空字符已存在。 c_str
的实现无法返回指向新数组的指针,因此数组必须存储在std::string
对象本身上。
因此,您有两种有效的方法来实现它:
'\0'
存储在构造中_Myptr()
字符数组的末尾,或'\0'
时添加c_str()
,并在析构函数中删除副本。第一种方法允许您为_Myptr()
返回c_str()
,代价是为每个字符串存储一个额外字符。第二种方法需要每个std::string
对象有一个额外的指针,因此第一种方法更便宜。
答案 1 :(得分:5)
在C ++ 11之前,没有要求std::string
(或模板化的类std::basic_string
- 其中std :: string是实例化)存储尾随'\0'
。这反映在data()
和c_str()
成员函数的不同规范中 - data()
返回指向基础数据的指针(不需要以'\0'
终止, c_str()
返回了一个带有终止'\0'
的副本。但是,同样地,没有要求不在内部存储尾随'\0'
(访问存储数据末尾的字符是未定义的行为) .....并且,为简单起见,一些实现选择追加尾随'\0'
。
使用C ++ 11,这改变了。实质上,data()
成员函数被指定为与c_str()
具有相同的效果(即返回的指针指向具有尾随'\0'
的数组的第一个字符)。这导致要求'\0'
返回的数组上跟踪data()
,因此要求内部表示。
因此,您所看到的行为与C ++ 11一致 - 该类的一个不变量是尾随'\0'
(即构造函数确保是这种情况,修改字符串的成员函数确保它仍然是真实的,并且所有公共成员职能都可以依赖它是真实的。)
您看到的行为与C ++ 11之前的C ++标准并不矛盾。严格地说,在C ++ 11之前std::string
不需要保持尾随'\0'
,但同样地,实施者可以选择这样做。
答案 2 :(得分:1)
要求是c_str
必须返回空终止的cstring。没有什么可以说函数必须添加null终止符。大多数实现(我认为所有想要符合标准的实现)都将null终止符存储在字符串本身使用的底层缓冲区中。其中一个原因是
std::string s;
assert(s[0] == '\0');
必须工作,因为现在需要字符串返回string[string.size()]
处的空终止符。如果string没有在底层缓冲区中存储空终止符,那么[]
必须进行边界检查以查看它是否在size()
并且需要返回\0
。