字符串::c_str() 是否不再以 C++11 结尾的空值
Is string::c_str() no longer null terminated in C++11?
在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_str
和 data
都必须为 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;
- 返回:一个指针 p,以便
[0,size()]
中的每个i
p + i == &operator[](i)
。- 复杂度:恒定时间。
- 要求:程序不得更改字符数组中存储的任何值。
"历史"是很久以前,当每个人都在单线程中工作时,或者至少线程是使用自己的数据的工作线程,他们为C++设计了一个字符串类,这使得字符串处理比以前更容易,并且他们重载了 operator+ 来连接字符串。
问题是用户会执行以下操作:
s = s1 + s2 + s3 + s4;
并且每个串联都会创建一个必须实现字符串的临时。
因此,有人有"懒惰评估"的脑电波,这样在内部你可以存储某种带有所有字符串的"绳索",直到有人想把它读成一个C字符串,此时你会将内部表示更改为一个连续的缓冲区。
这解决了上述问题,但引起了其他麻烦的负载,特别是在多线程世界中,人们期望 .c_str(( 操作是只读的/不会更改任何内容,因此无需锁定任何内容。在类实现中过早的内部锁定以防万一有人在多线程上做(当甚至没有线程标准时(也不是一个好主意。事实上,做任何事情的成本都比每次都简单地复制缓冲区要高。与字符串实现放弃"写入时复制"实现的原因相同。
因此,使.c_str()
成为真正不可变的操作被证明是最明智的做法,但是是否可以在现在线程感知的标准中"依赖"它?因此,新标准决定明确声明您可以,因此内部表示需要保留空终止符。
很好发现。 这当然是最近采用的标准中的一个缺陷;我确定无意破坏当前使用 c_str
的所有代码。 我建议一份缺陷报告,或者至少在comp.std.c++
中提出问题(如果涉及缺陷,通常会在委员会面前结束(。