string :: c_str()在C++ 11中不再以null结尾吗?

Man*_*rse 72 c++ string c++11

在C++中,11 basic_string::c_str被定义为完全相同basic_string::data,而后者又定义为*(begin() + n)*(&*begin() + n)(when 0 <= n < size())完全相同.

我找不到任何要求字符串在其末尾始终具有空字符的内容.

这是否意味着c_str()不再保证生成以null结尾的字符串?

Mik*_*kov 80

字符串现在需要在内部使用以null结尾的缓冲区.看看operator[](21.4.5)的定义:

要求: pos <= size().

返回: *(begin() + pos) if pos < size(),否则引用T具有值 的类型的对象charT(); 参考值不得修改.

回顾c_str(21.4.7.1/1),我们看到它的定义是operator[]:

返回:一个指针p,p + i == &operator[](i)用于每个iin [0,size()].

两者c_strdata需要是O(1),因此实现有效被迫使用空值终止的缓冲区.

另外,正如DavidRodríguez - dribeas在评论中指出的那样,返回值要求也意味着你可以使用它&operator[](0)作为同义词c_str(),因此终止空字符必须位于同一个缓冲区中(因为*(p + size())必须等于charT()); 这也意味着即使终结器被懒惰地初始化,也不可能在中间状态下观察缓冲区.

  • 虽然这并不是说字符串必须以null结尾,但可以从字符串要求中推断出来.`c_str`和`data`都必须是O(1)操作,这意味着它们无法动态创建副本.另外,匹配`operator []`输出的要求意味着它已经被nul终止,或者对`data` /`c_str`的​​调用必须在返回指针之前添加nul终止符.此外,在调用之前,字符串必须具有该终结符*的空间以维持O(1)要求.从技术上讲,字符串不必是nul终止,但`data()`确实如此 (21认同)
  • @jalf:*这并没有说明字符串被空终止.*是的,确实如此.21.4.7.1表示`c_str()`返回的指针必须指向长度为`size()+ 1`的缓冲区.21.4.5表示此缓冲区的最后一个元素必须具有值`charT()` - 换句话说,空字符. (9认同)
  • 另外,最后一个引用:*返回:指针p,使得p + i ==&operator [](i)为[0,size()]中的每个i.*表示`&operator [](size())= =&operator [](size() - 1)+ 1` - 如果`operator [](size())`返回对字符串外部的`\ 0`的引用,则永远不能满足此要求. (7认同)
  • 这并没有说明字符串是否为空终止. (6认同)
  • @jalf:"*这个答案只给我们一半的推理链.*"它给出了完整链的三分之二.缺少的一件事是默认初始化`charT()`分配的值是空字符.当`charT`是`char`时,情况就是这样.关于`wchar_t`的含义,标准有点模糊(有点模糊). (4认同)
  • 由于`c_str`和`data`都需要是恒定时间,IMO这几乎迫使实现使用以null结尾的缓冲区. (2认同)
  • @David和其他人:米哈伊尔发布的(第一个)片段没有说明空值,也没有关于缓冲区本身是空终止的.我的观点很简单,他说字符串需要在内部使用null-termianted缓冲区,然后发布标准中引用完全不同的引用.即使使用第二个片段,它也没有说任何关于缓冲区本身是空终止的. (2认同)
  • 鉴于OP的问题基本上是"字符串被空终止的要求在哪里",我希望答案指向标准中至少提到空的部分.在这个答案中我可以看到`operator []`(你的输出你注意到`c_str`需要匹配)的结果必须在字符串的末尾返回一个null?这个答案只给我们一半的推理链.它告诉我们`c_str`需要返回与其他东西相同的东西,这在答案中没有定义. (2认同)
  • @Mankarse 21.4.5说`front()`相当于`operator [](0)`,所以你的例子仍然返回null(因为`&operator [](0)`相当于`c_str()`).如果你使用`begin()`而不是`front()`,你将有效地解引用`end()`,这是未定义的. (2认同)
  • 这个论点取决于O(1)时间,这并不意味着`c_str()`和`data`不能进行实际处理.实际上,人们可以设想一种缓冲技术,其中末尾的固定数量的字符存储在单独的缓冲区中并在调用`c_str()`时复制(因为固定的数量,技术上仍然是O(1)).也许字符串使用某种可重定位的OS内存,并且调用`c_str()`需要固定内存(以防止移动).虽然它必须以某种方式在内部被终止,但我不同意`front`的地址是`c_str`的​​同义词. (2认同)
  • @ edA-qa mort-ora-y在`&front()`相当于`c_str()`的问题上 - 21.4.5/9将`front()`定义为等同于`operator [](0)`和21.4.7.1/1表示`c_str()`与`&operator [](0)`相同.请参阅我对Mankarse的回复. (2认同)

seh*_*ehe 23

嗯,实际上新标准确实规定.data()和.c_str()现在是同义词.但是,它并没有说.c_str()不再是零终止:)

它只是意味着你现在可以依赖.data()也是零终止的.

论文N2668定义了std :: basic_string的c_str()和data()成员,如下所示:

 const charT* c_str() const; 
 const charT* data() const; 
Run Code Online (Sandbox Code Playgroud)

返回:指向长度为size()+ 1的数组的初始元素的指针,其第一个size()元素等于由*this控制的字符串的相应元素,其最后一个元素是charT()指定的空字符.

要求:程序不得更改存储在字符数组中的任何值.

请注意,这意味着任何有效的std :: string可以作为一个C字符串来对待,因为的std :: string可以包含嵌入的空值,直接作为一个const char*时将提前结束的C字符串.

附录:

我无法访问C++ 11的实际发布的最终规范,但似乎确实在规范的修订历史中某处删除了措辞:例如http://www.open-std.org/jtc1/ SC22/WG21 /文档/文件/ 2011/n3242.pdf

§21.4.7basic_string字符串操作 [string.ops]

§21.4.7.1basic_string访问器 [string.accessors]

     const charT* c_str() const noexcept;
     const charT* data() const noexcept;
Run Code Online (Sandbox Code Playgroud)
  1. 返回:指针p,p + i == &operator[](i)对于每个iin [0,size()].
  2. 复杂性:恒定时间.
  3. 要求:程序不得更改存储在字符数组中的任何值.


Cas*_*Cow 10

"历史"是很久以前当每个人都在单线程中工作,或者至少线程是拥有自己数据的工作者时,他们为C++设计了一个字符串类,它使字符串处理比以前更容易,并且它们重载了operator +来连接字符串.

问题是用户会做类似的事情:

s = s1 + s2 + s3 + s4;
Run Code Online (Sandbox Code Playgroud)

并且每个连接都会创建一个必须实现字符串的临时连接.

因此,有人有"懒惰评估"的脑波,这样在内部你可以存储某种带有所有字符串的"绳子",直到有人想把它作为C字符串读取,此时你会将内部表示改为连续的缓冲区.

这解决了上面的问题但是引起了其他令人头疼的问题,特别是在多线程世界中,人们期望.c_str()操作是只读的/不会改变任何东西,因此不需要锁定任何东西.在类实现中过早内部锁定以防万一有人在多线程中执行它(当时甚至没有线程标准)也不是一个好主意.事实上,除了每次复制缓冲区之外,做任何事情都要花费更多.出于字符串实现,放弃了"copy on write"实现的相同原因.

因此,制作.c_str()一个真正不可变的操作是最明智的做法,但是可以在一个现在是线程感知的标准中"依赖"它吗?因此,新标准决定明确说明您可以,因此内部表示需要保持空终止符.