在C ++ 11中,basic_string::c_str
被定义为与basic_string::data
完全相同,后者被定义为与*(begin() + n)
和*(&*begin() + n)
完全相同(当0 <= n < size()
)。
我找不到任何要求字符串在其末尾始终具有空字符的内容。
这是否意味着c_str()
不再保证产生以空字符结尾的字符串?
答案 0 :(得分:80)
现在需要字符串在内部使用以null结尾的缓冲区。看看operator[]
(21.4.5)的定义:
需要:
pos <= size()
。返回:
*(begin() + pos)
ifpos < size()
,否则引用T
类型的对象charT()
;参考值不得修改。
回顾c_str
(21.4.7.1/1),我们发现它是根据operator[]
定义的:
返回:指针
p
,p + i == &operator[](i)
中的每个i
[0,size()]
。
c_str
和data
都需要为O(1),因此实现强制使用以null结尾的缓冲区。
此外,正如David Rodríguez - dribeas在评论中指出的那样,返回值要求也意味着您可以使用&operator[](0)
作为c_str()
的同义词,因此终止空字符必须位于相同的缓冲区(因为*(p + size())
必须等于charT()
);这也意味着即使终结符被懒惰地初始化,也不可能在中间状态下观察缓冲区。
答案 1 :(得分:23)
实际上,新标准确实规定.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控制,其最后一个元素是a charT()指定的null字符。
要求:程序不得更改存储的任何值 字符数组。
请注意,这样做 NOT 意味着任何有效的std :: string都可以被视为C字符串,因为std :: string可以包含嵌入的空值,这会在以后过早地结束C字符串直接用作const char *。
我无法访问实际发布的final spec of C++11,但似乎确实在规范的修订历史中某处删除了措辞:例如http://www.open-std.org/jtc1/sc22/wg21/docs/papers/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;
- 返回:指针p,
p + i == &operator[](i)
中每个i
的{{1}}。- 复杂性:恒定时间。
- 要求:程序不得更改存储在字符数组中的任何值。
醇>
答案 2 :(得分:10)
“历史”是很久以前当每个人都在单线程中工作,或者至少线程是拥有自己数据的工作者时,他们为C ++设计了一个字符串类,使得字符串处理比以前更容易,并且他们重载了operator +来连接字符串。
问题是用户会做类似的事情:
s = s1 + s2 + s3 + s4;
并且每个连接都会创建一个必须实现字符串的临时连接。
因此,某人有了“懒惰评价”的脑波,这样你就可以在内部存储所有字符串的某种“绳子”,直到有人想把它作为C字符串读出来,此时你要将内部表示改为一个连续的缓冲区。
这解决了上面的问题但引起了其他令人头疼的问题,特别是在多线程世界中,人们期望.c_str()操作是只读的/不会改变任何东西,因此不需要锁定任何东西。在类实现中过早内部锁定以防万一有人在多线程中执行它(当时甚至没有线程标准)也不是一个好主意。事实上,除了每次复制缓冲区之外,做任何事情都要花费更多。与字符串实现相同的原因是“写入时复制”实现。
因此,使.c_str()
成为一个真正不可变的操作是最明智的做法,但是可以在一个现在是线程感知的标准中“依赖”它吗?因此,新标准决定明确说明你可以,因此内部表示需要保持空终止符。
答案 3 :(得分:2)
很好看。这肯定是最近采用的标准的缺陷;我确信没有意图打破目前使用c_str
的所有代码。我会建议一个缺陷报告,或至少在comp.std.c++
中提出问题(如果涉及缺陷,通常会在委员会面前结束)。