在C ++ 11中,basic_string::c_str
它定义为与完全相同basic_string::data
,而后者又定义为*(begin() + n)
与和*(&*begin() + n)
(when 0 <= n < size()
)完全相同。
我找不到任何要求字符串末尾始终包含空字符的内容。
这是否意味着c_str()
不再保证会产生以null结尾的字符串?
在C ++ 11中,basic_string::c_str
它定义为与完全相同basic_string::data
,而后者又定义为*(begin() + n)
与和*(&*begin() + n)
(when 0 <= n < size()
)完全相同。
我找不到任何要求字符串末尾始终包含空字符的内容。
这是否意味着c_str()
不再保证会产生以null结尾的字符串?
c_str
未返回以NULL结尾的字符串,则它将是有史以来命名最错误的函数。
=
in 0 <= n <= size()
…包括标准包一样,一切都很好
Answers:
现在需要字符串才能在内部使用以空值结尾的缓冲区。看一下operator[]
(21.4.5)的定义:
要求:
pos <= size()
。返回:
*(begin() + pos)
如果pos < size()
,否则返回对T
带有value 类型的对象的引用charT()
;参考值不得修改。
回顾c_str
(21.4.7.1/1),我们看到它的定义如下operator[]
:
返回:
p
使得in中p + i == &operator[](i)
的每个指针都返回的指针。i
[0,size()]
并且c_str
和data
都必须为O(1),因此有效地强制实现使用以空值结尾的缓冲区。
此外,正如DavidRodríguez-dribeas在评论中指出的那样,返回值要求还意味着您可以&operator[](0)
用作的同义词c_str()
,因此终止的空字符必须位于相同的缓冲区中(因为*(p + size())
必须等于charT()
);这也意味着即使终止符被延迟初始化,也无法观察中间状态的缓冲区。
c_str
并data
必须是O(1)操作,这意味着它们不能动态创建一个副本。另外,匹配operator[]
输出的要求意味着它已经被nul终止,或者对data
/的调用c_str
必须在返回指针之前添加nul终止符。此外,在调用之前,字符串必须具有用于该终止符的空间,以保持O(1)要求。从技术上讲,串不一定是NUL终止,但data()
确实
&operator[](size()) == &operator[](size()-1) + 1
如果operator[](size())
返回\0
对字符串外部的引用,则永远无法满足该要求。
c_str()
必须指向length的缓冲区size()+1
。21.4.5说此缓冲区的最后一个元素必须具有值charT()
-换句话说,是空字符。
charT()
是空字符。这显然是的情况下charT
是char
。该标准对的含义有点含糊(不多含糊)wchar_t
。
好吧,事实上,新标准确实规定.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可以包含嵌入的空值,直接作为一个const char时将提前结束的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,使得in中
p + i == &operator[](i)
的每个指针。i
[0,size()]
- 复杂度:恒定时间。
- 要求:程序不得更改存储在字符数组中的任何值。
Operator[](i)
在帖子中的方括号,因为当前它们被解释为链接,这使文本难以理解。
“历史”是很久以前,当每个人都在单个线程中工作,或者至少线程是拥有自己数据的工人时,他们为C ++设计了一个字符串类,该类使字符串处理比以前更容易,并且它们重载了运算符+连接字符串。
问题是用户将执行以下操作:
s = s1 + s2 + s3 + s4;
并且每个串联都会创建一个临时对象,该临时对象必须实现一个字符串。
因此,有人会产生“惰性评估”的头脑,以便在内部可以将所有字符串存储在某种“绳子”中,直到有人想要将其读取为C字符串为止,此时您可以将内部表示形式更改为连续的缓冲区。
这解决了上面的问题,但引起了其他麻烦,特别是在多线程世界中,人们期望.c_str()操作为只读/不更改任何内容,因此无需锁定任何内容。在类实现中过早地进行内部锁定,以防万一有人对它进行多线程处理(甚至在没有线程标准的情况下)也不是一个好主意。实际上,做任何事情都比每次简单地复制缓冲区要昂贵得多。字符串实现放弃了“写时复制”实现的相同原因。
因此,做.c_str()
一个真正不变的操作被认为是最明智的选择,但是,人们是否可以在现在意识到线程的标准中“依赖”它呢?因此,新标准决定明确声明您可以,因此内部表示形式必须包含空终止符。
string
还具有奇怪的属性,即第一个非constbegin()
会使迭代器无效!
发现得好。这肯定是最近采用的标准中的一个缺陷。我确定没有意图破坏当前使用的所有代码c_str
。我建议提出缺陷报告,或者至少提出问题comp.std.c++
(如果它涉及缺陷,通常会提交给委员会)。
21.4.2/2
说.data()
一个空字符串实际上不是以空字符结尾的(.data()+1
无效,但是应该是一个超出该指针的指针\0
)