std::string::c_str & 空终止
std::string::c_str & Null termination
我阅读了 std::string::c_str
的各种描述,包括在 years/decades、
上针对 SO 提出的问题
我喜欢这个描述的清晰度:
Returns a pointer to an array that contains a null-terminated sequence
of characters (i.e., a C-string) representing the current value of the
string object. This array includes the same sequence of characters
that make up the value of the string object plus an additional
terminating null-character ('[=23=]') at the end.
然而,关于此功能的一些用途仍不清楚。
您可能会认为调用 c_str
可能会在存储在主机对象内部字符数组中的字符串末尾添加一个 [=15=]
字符(std::string
):
s[s.size+1] = '[=11=]'
但似乎 std::string
对象在调用 c_str
之前默认为 Null 终止:
查看定义后:
const _Elem *c_str() const _NOEXCEPT
{ // return pointer to null-terminated nonmutable array
return (this->_Myptr());
}
我没有看到任何将 [=15=]
添加到 char 数组末尾的代码。据我所知 c_str
只是 returns 一个指向存储在数组第一个元素中的 char 的指针,这与 begin()
非常相似。我什至没有看到检查内部数组是否由 [=15=]
终止的代码
还是我遗漏了什么?
要求是 c_str
必须 return 一个空终止的 cstring。没有说函数必须添加空终止符。大多数实现(我认为所有想要符合标准的实现)都将空终止符存储在字符串本身使用的底层缓冲区中。原因之一是
std::string s;
assert(s[0] == '[=10=]');
必须工作,因为 return string[string.size()]
处的空终止符现在需要字符串。如果字符串没有将空终止符存储在底层缓冲区中,那么 []
将不得不进行边界检查以查看它是否位于 size()
并且需要 return [=15=]
.
您看不到将 '[=10=]'
添加到序列末尾的代码,因为空字符已经存在。 c_str
的实现不能 return 指向新数组的指针,因此数组必须存储在 std::string
对象本身上。
因此,您有两种有效的实现方法:
- 在构造时始终将
'[=10=]'
存储在 _Myptr()
个字符数组的末尾,或者
- 按需复制字符串,在调用
c_str()
时添加'[=10=]'
,并在析构函数中删除副本。
第一种方法让您 return _Myptr()
得到 c_str()
,但要为每个字符串存储一个额外的字符。第二种方法需要每个 std::string
对象一个额外的指针,因此第一种方法更便宜。
在 C++11 之前,不要求 std::string
(或模板化的 class std::basic_string
- 其中 std::string 是一个实例)存储一个尾随 '[=12=]'
。这反映在 data()
和 c_str()
成员函数的不同规范中 - data()
returns 指向基础数据的指针(不需要以 '[= 12=]'
和 c_str()
返回了一个带有终止符 '[=12=]'
的副本。但是,同样地,没有要求不在内部存储尾随 '[=12=]'
(访问存储数据末尾之后的字符是未定义的行为)......而且,为了简单起见,一些实现选择无论如何附加尾随 '[=12=]'
。
在 C++11 中,这发生了变化。本质上,data()
成员函数被指定为提供与 c_str()
相同的效果(即返回的指针指向具有尾随 '[=12=]'
的数组的第一个字符)。这导致 data()
返回的数组需要尾随 '[=12=]'
,因此在内部表示中。
所以您看到的行为与 C++11 一致 - class 的不变量之一是尾随 '[=12=]'
(即构造函数确保情况如此,成员函数修改字符串确保它保持为真,并且所有 public 成员函数都可以依赖它为真)。
您看到的行为与 C++11 之前的 C++ 标准并不矛盾。严格来说,C++11 之前的 std::string
不需要维护尾随 '[=12=]'
,但同样,实现者可以选择这样做。
我阅读了 std::string::c_str
的各种描述,包括在 years/decades、
我喜欢这个描述的清晰度:
Returns a pointer to an array that contains a null-terminated sequence of characters (i.e., a C-string) representing the current value of the string object. This array includes the same sequence of characters that make up the value of the string object plus an additional terminating null-character ('[=23=]') at the end.
然而,关于此功能的一些用途仍不清楚。
您可能会认为调用 c_str
可能会在存储在主机对象内部字符数组中的字符串末尾添加一个 [=15=]
字符(std::string
):
s[s.size+1] = '[=11=]'
但似乎 std::string
对象在调用 c_str
之前默认为 Null 终止:
查看定义后:
const _Elem *c_str() const _NOEXCEPT
{ // return pointer to null-terminated nonmutable array
return (this->_Myptr());
}
我没有看到任何将 [=15=]
添加到 char 数组末尾的代码。据我所知 c_str
只是 returns 一个指向存储在数组第一个元素中的 char 的指针,这与 begin()
非常相似。我什至没有看到检查内部数组是否由 [=15=]
还是我遗漏了什么?
要求是 c_str
必须 return 一个空终止的 cstring。没有说函数必须添加空终止符。大多数实现(我认为所有想要符合标准的实现)都将空终止符存储在字符串本身使用的底层缓冲区中。原因之一是
std::string s;
assert(s[0] == '[=10=]');
必须工作,因为 return string[string.size()]
处的空终止符现在需要字符串。如果字符串没有将空终止符存储在底层缓冲区中,那么 []
将不得不进行边界检查以查看它是否位于 size()
并且需要 return [=15=]
.
您看不到将 '[=10=]'
添加到序列末尾的代码,因为空字符已经存在。 c_str
的实现不能 return 指向新数组的指针,因此数组必须存储在 std::string
对象本身上。
因此,您有两种有效的实现方法:
- 在构造时始终将
'[=10=]'
存储在_Myptr()
个字符数组的末尾,或者 - 按需复制字符串,在调用
c_str()
时添加'[=10=]'
,并在析构函数中删除副本。
第一种方法让您 return _Myptr()
得到 c_str()
,但要为每个字符串存储一个额外的字符。第二种方法需要每个 std::string
对象一个额外的指针,因此第一种方法更便宜。
在 C++11 之前,不要求 std::string
(或模板化的 class std::basic_string
- 其中 std::string 是一个实例)存储一个尾随 '[=12=]'
。这反映在 data()
和 c_str()
成员函数的不同规范中 - data()
returns 指向基础数据的指针(不需要以 '[= 12=]'
和 c_str()
返回了一个带有终止符 '[=12=]'
的副本。但是,同样地,没有要求不在内部存储尾随 '[=12=]'
(访问存储数据末尾之后的字符是未定义的行为)......而且,为了简单起见,一些实现选择无论如何附加尾随 '[=12=]'
。
在 C++11 中,这发生了变化。本质上,data()
成员函数被指定为提供与 c_str()
相同的效果(即返回的指针指向具有尾随 '[=12=]'
的数组的第一个字符)。这导致 data()
返回的数组需要尾随 '[=12=]'
,因此在内部表示中。
所以您看到的行为与 C++11 一致 - class 的不变量之一是尾随 '[=12=]'
(即构造函数确保情况如此,成员函数修改字符串确保它保持为真,并且所有 public 成员函数都可以依赖它为真)。
您看到的行为与 C++11 之前的 C++ 标准并不矛盾。严格来说,C++11 之前的 std::string
不需要维护尾随 '[=12=]'
,但同样,实现者可以选择这样做。