字符串::c_str() 是否不再以 C++11 结尾的空值

Is string::c_str() no longer null terminated in C++11?

本文关键字:C++11 结尾 空值 不再 str 字符串 是否      更新时间:2023-10-16

在C++11中,basic_string::c_str被定义为与basic_string::data完全相同,而又被定义为与*(begin() + n)*(&*begin() + n)完全相同(当0 <= n < size()时(。

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

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

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

要求:pos <= size()

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

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

返回:p指针,以便[0,size()]中的每个i p + i == &operator[](i)

并且 c_strdata 都必须为 O(1(,因此实现实际上被强制使用以 null 结尾的缓冲区。

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

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

这只是意味着您现在可以依赖 .data(( 也以零终止。

论文 N2668 将 std::basic_string 的 c_str(( 和 data(( 成员定义为 遵循:

 const charT* c_str() const; 
 const charT* data() const; 

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

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

请注意,这并不意味着任何有效的 std::string 都可以被视为 C 字符串,因为 std::string 可以包含嵌入的空值,当直接用作常量字符*时,这将过早地结束 C 字符串。

补遗:

我无法访问实际发布的 C++11 最终规范,但似乎确实在规范的修订历史中的某个地方删除了措辞:例如 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf

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

§ 21.4.7.1 basic_string 访问器[string.accessors]

     const charT* c_str() const noexcept;
     const charT* data() const noexcept;
  1. 返回:一个指针 p,以便[0,size()]中的每个i p + i == &operator[](i)
  2. 复杂度:恒定时间。
  3. 要求:程序不得更改字符数组中存储的任何值。

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

问题是用户会执行以下操作:

s = s1 + s2 + s3 + s4;

并且每个串联都会创建一个必须实现字符串的临时。

因此,有人

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

这解决了上述问题,但引起了其他麻烦的负载,特别是在多线程世界中,人们期望 .c_str(( 操作是只读的/不会更改任何内容,因此无需锁定任何内容。在类实现中过早的内部锁定以防万一有人在多线程上做(当甚至没有线程标准时(也不是一个好主意。事实上,做任何事情的成本都比每次都简单地复制缓冲区要高。与字符串实现放弃"写入时复制"实现的原因相同。

因此,使.c_str()成为真正不可变的操作被证明是最明智的做法,但是是否可以在现在线程感知的标准中"依赖"它?因此,新标准决定明确声明您可以,因此内部表示需要保留空终止符。

很好发现。 这当然是最近采用的标准中的一个缺陷;我确定无意破坏当前使用 c_str 的所有代码。 我建议一份缺陷报告,或者至少在comp.std.c++中提出问题(如果涉及缺陷,通常会在委员会面前结束(。