basic_string::c_str
被定义为与basic_string::data
完全相同,而basic_string::data
则被定义为恰好等同于*(begin() + n)
和*(&*begin() + n)
(当 0 <= n < size()
)。我没有找到任何要求该字符串必须始终以空字符结尾的内容。
这是否意味着不能保证
c_str()
将产生以空字符结尾的字符串?operator[]
(21.4.5)的定义:
回顾Requires:
pos <= size()
.Returns: 如果
pos < size()
,则返回*(begin() + pos)
,否则返回类型为T
的对象的引用,该对象具有值charT()
;不得修改所引用的值。
c_str
(21.4.7.1/1),我们看到它是以operator[]
为基础定义的:
而且Returns: 指针
p
,使得对于[0,size()]
中的每个i
,都有p + i == &operator[](i)
。
c_str
和data
都需要O(1),因此实现效果被迫使用以null结尾的缓冲区。&operator[](0)
用作c_str()
的同义词,因此终止null字符必须位于同一缓冲区中(因为*(p + size())
必须等于charT()
);这也意味着即使惰性初始化终止符,也无法观察缓冲区在中间状态下的情况。c_str
和data
操作必须是O(1)的,这意味着它们不能在运行时创建副本。此外,匹配operator[]
输出的要求意味着它已经以null结尾,或者在返回指针之前调用data
/c_str
时必须添加null终止符号。另外,在调用时,字符串必须有足够的空间来存储终止符以满足O(1)的要求。从技术上讲,字符串并不需要以null结尾,但data()
操作会自动添加。 - David Rodríguez - dribeas&operator[](size()) == &operator[](size()-1) + 1
--也就是说,如果operator[](size())
返回了指向字符串外部的\0
的引用,那么这个要求将永远无法满足。 - David Rodríguez - dribeasc_str()
返回的指针必须指向一个长度为size()+1
的缓冲区。而21.4.5则表明该缓冲区的最后一个元素必须是charT()
,也就是空字符。 - David HammencharT()
赋予的值是空字符。当 charT
是char
时,显然如此。标准对wchar_t
的含义有些模糊(比有些模糊还要更模糊)。 - David Hammen实际上,新标准规定了.data()和.c_str()现在是同义词。然而,它并未说明.c_str()不再以零结尾 :)
这意味着您现在也可以依赖于.data()以零结尾。
请注意,这并不意味着任何有效的std::string都可以被视为C字符串,因为std::string可能包含嵌入的null,直接将其用作const char*时会过早地结束C字符串。Paper N2668 defines c_str() and data() members of std::basic_string as follows:
const charT* c_str() const; const charT* data() const;
Returns: A pointer to the initial element of an array of length size() + 1 whose first size() elements equal the corresponding elements of the string controlled by *this and whose last element is a null character specified by charT().
Requires: The program shall not alter any of the values stored in the character array.
§ 21.4.7基本字符串操作
[string.ops]
§ 21.4.7.1基本字符串访问器
[string.accessors]
const charT* c_str() const noexcept; const charT* data() const noexcept;
- Returns: A pointer p such that
p + i == &operator[](i)
for eachi
in[0,size()]
.- Complexity: constant time.
- Requires: The program shall not alter any of the values stored in the character array.
Operator[](i)
中的方括号,因为它们当前被解释为链接,这使得文本难以理解。 - Kevin Cathcart"历史"是这样的,很久以前当每个人都在单线程工作时,或者至少线程是带有自己数据的工作者,他们为C++设计了一个字符串类,使字符串处理比以前更容易,并重载了operator+来连接字符串。
问题在于用户会做类似以下的操作:
s = s1 + s2 + s3 + s4;
每次串联字符串都会创建一个临时变量,必须实现一个字符串,因此有人想到了“惰性评估”:在内部可以存储某种带有所有字符串的“绳索”,直到有人想要将其作为C字符串读取,此时您将更改内部表示以形成连续缓冲区。
这解决了上述问题,但引起了许多其他问题,特别是在多线程世界中,在那里人们期望.c_str()操作是只读的/不会改变任何内容,因此不需要锁定任何内容。在类实现中过早进行内部锁定以防止某些人进行多线程处理(当时甚至没有线程标准)也不是一个好主意。实际上,与其做任何这样的事情,每次复制缓冲区都更加昂贵。出于同样的原因,“写时复制”实现被放弃用于字符串实现。
因此,使.c_str()
成为一个真正的不可变操作是最明智的选择,但在现在已经具备线程感知能力的标准中,是否可以“依赖”它?因此,新标准决定明确说明您可以这样做,因此内部表示需要保存空终止符。
string
还有一个奇怪的特性,即第一个非 const 的 begin()
会使迭代器失效! - curiousguy非常敏锐的发现。这显然是最近采用的标准中的一个缺陷;我相信并没有意图破坏当前使用c_str的所有代码。我建议您提交缺陷报告,或者至少在comp.std.c++
中提出问题(如果涉及缺陷,则通常会提交给委员会)。
21.4.2/2
表示空字符串的.data()
实际上并没有以null结尾(.data()+1
不是有效的指针,但应该是\0
之后的一个指针)。 - MSalters
c_str
函数不能返回以NULL结尾的字符串,那它将成为最容易误解的函数名称。 - Seth Carnegie0 <= n <= size()
中漏掉了一个=
... 当你加上它时,一切都会正常,就像标准所做的那样。 - Ben Voigt