在 C + + 11中,basic_string::c_str被定义为与 basic_string::data完全相同,而 basic_string::data又被定义为与 *(begin() + n)和 *(&*begin() + n)(当 0 <= n < size()时)完全相同。
basic_string::c_str
basic_string::data
*(begin() + n)
*(&*begin() + n)
0 <= n < size()
我找不到任何要求字符串的末尾总是有一个空字符的内容。
这是否意味着 c_str()不再保证能够生产空终止字符串?
c_str()
那么实际上新标准确实规定了,。()及。C _ str ()现在是同义词。然而,它并没有说。C _ str ()不再以零结尾:)
这意味着您现在也可以依赖. data ()以零结尾。
论文 N2668将 std: : basic _ string 的 c _ str ()和 data ()成员定义为 如下: const charT* c_str() const; const charT* data() const; Return: 指向长度数组的初始元素的指针 Size () + 1,其第一个 size ()元素等于对应的 由 * this 控制的字符串的元素,其最后一个元素是 由 charT ()指定的空字符。 要求: 程序不得更改存储在 字符数组。
论文 N2668将 std: : basic _ string 的 c _ str ()和 data ()成员定义为 如下:
const charT* c_str() const; const charT* data() const;
Return: 指向长度数组的初始元素的指针 Size () + 1,其第一个 size ()元素等于对应的 由 * this 控制的字符串的元素,其最后一个元素是 由 charT ()指定的空字符。
要求: 程序不得更改存储在 字符数组。
注意,这意味着任何有效的 std: : string 都可以被视为 C-string,因为 std: : string 可以包含嵌入的 null,当直接用作 const char * 时,这将提前结束 C-string。
我不能访问实际发布的 C + + 11最终规范,但是看起来确实在规范的修订历史中丢失了一些措辞: 例如 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
21.4.7 basic _ string 字符串运算 21.4.7.1 basic _ string 访问器 [string.accessors]
21.4.7 basic _ string 字符串运算
21.4.7.1 basic _ string 访问器 [string.accessors]
[string.accessors]
const charT* c_str() const noexcept; const charT* data() const noexcept; 返回: 一个指针 p,使得 [0,size()]中每个 i的 p + i == &operator[](i)。 复杂性: 恒定的时间。 要求: 程序不能改变字符数组中存储的任何值。
const charT* c_str() const noexcept; const charT* data() const noexcept;
[0,size()]
i
p + i == &operator[](i)
好眼力。这当然是最近采用的标准中的一个缺陷; 我确信没有打算破坏当前使用 c_str的所有代码。我会建议一个缺陷报告,或者至少在 comp.std.c++中提出这个问题(如果它涉及到一个缺陷,通常会在委员会面前结束)。
c_str
comp.std.c++
字符串现在需要在内部使用以 null 结尾的缓冲区:
要求:。 返回: *(begin() + pos) if < code > pos < Size () ,否则引用值为 T类型的对象 参考值不得修改。
要求:。
返回: *(begin() + pos) if < code > pos < Size () ,否则引用值为 T类型的对象 参考值不得修改。
*(begin() + pos)
T
回顾 c_str(21.4.7.1/1) ,我们发现它是根据 operator[]来定义的:
operator[]
返回: 一个指针 p,使得 [0,size()]中每个 i的 p + i == &operator[](i)。
p
而且 c_str和 data都必须是 O (1) ,因此实现实际上被迫使用以 null 结尾的缓冲区。
data
此外,正如 David Rodríguez-Dribeas在评论中指出的那样,返回值的要求也意味着你可以使用 &operator[](0)作为 c_str()的同义词,所以结束空字符必须位于同一个缓冲区中(因为 *(p + size())必须等于 charT()) ,这也意味着即使结束符是延迟初始化的,也不可能观察到居间态中的缓冲区。
&operator[](0)
*(p + size())
charT()
“历史”是很久以前,当每个人都在单线程中工作,或者至少每个线程都是有自己数据的工作者时,他们为 C + + 设计了一个字符串类,这使得字符串处理比以前更容易,他们重载了操作符 + 来连接字符串。
问题在于,用户可能会这样做:
s = s1 + s2 + s3 + s4;
每个串联都会创建一个临时的,必须实现一个字符串。
因此,有人想到了“延迟计算”,这样你就可以在内部存储某种包含所有字符串的“绳子”,直到有人想把它读成 C 字符串,这时你就可以把内部表示变成一个连续的缓冲区。
这解决了上面的问题,但是引起了其他一些令人头疼的问题,特别是在多线程世界中,人们期望。C _ str ()操作为只读/不会更改任何内容,因此不需要锁定任何内容。在类实现中过早地进行内部锁定以防有人进行多线程操作(当时甚至没有线程标准)也不是一个好主意。实际上,这样做的代价比每次只复制缓冲区要高。与字符串实现放弃“写时复制”实现的原因相同。
因此,使 .c_str()成为一个真正不可变的操作被证明是最明智的做法,然而,在一个现在是线程感知的标准中,人们能够“依赖”它吗?因此,新标准决定明确声明您可以,因此内部表示需要保存空终止符。
.c_str()