将 std::string 始终以 C++11 为空结尾
Will std::string always be null-terminated in C++11?
Herb Sutter在他的网站上2008年的一篇文章中指出:
出于与并发相关的原因,有一个积极的提议是在 C++0x 中进一步收紧这一点,并要求 null 终止并可能禁止写入时复制实现。论文实在是:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2534.html .我认为,本文件中的一项或两项提案可能会被采纳,但我们将在下两次会议上看到。
我知道 C++11 现在保证 std::string 内容被连续存储,但他们在最终草案中是否采用了上述内容?
现在使用像&str[0]
这样的东西安全吗?
是的。 根据 C++0x FDIS 21.4.7.1/1,std::basic_string::c_str()
必须返回
指针
p
,以便[0,size()]
中的每个i
p + i == &operator[](i)
。
这意味着给定一个字符串s
,s.c_str()
返回的指针必须与字符串中初始字符的地址相同(&s[0]
)。
&str[0]
可以安全使用 - 只要你不假设它指向以null结尾的字符串。
自 C++11 以来,要求包括(第 [string.accessors] 节):
-
str.data()
和str.c_str()
指向以 null 结尾的字符串。 -
&str[i]
==str.data() + i
, 表示0 <= i <= str.size()
- 请注意,这意味着存储是连续的。
但是,不要求&str[0] + str.size()
指向空终止符。
当调用 data()
、c_str()
或 operator[](str.size())
时,一个符合标准的实现必须将 null 终止符连续地放置在存储中;但是不需要将其放置在任何其他情况下,例如调用与其他参数一起operator[]
。
为了节省您阅读下面的长聊天讨论的时间: 有人提出反对意见,如果c_str()
要编写一个空终止符,它将在res.on.data.races#3下引起数据争用;我不同意这将是一场数据竞赛。
的 null 终止版本,但在混合 std::string 和 C char* 字符串时可能会C++惊喜。
空字符可能最终出现在 C++ std::string 中,这可能会导致细微的错误,因为 C 函数将看到较短的字符串。
错误代码可能会覆盖空终止符。这会导致未定义的行为。 然后,C 函数将读取字符串缓冲区之外的内容,这可能会导致崩溃。
#include <string>
#include <iostream>
#include <cstdio>
#include <cstring>
int main()
{
std::string embedded_null = "hellon";
embedded_null += ' ';
embedded_null += "worldn";
// C string functions finish early at embedded
std::cout << "C++ size: " << embedded_null.size()
<< " value: " << embedded_null;
printf("C strlen: %d value: %sn",
strlen(embedded_null.c_str()),
embedded_null.c_str());
std::string missing_terminator(3, 'n');
missing_terminator[3] = 'a'; // BUG: Undefined behaviour
// C string functions read beyond buffer and may crash
std::cout << "C++ size: " << missing_terminator.size()
<< " value: " << missing_terminator << 'n';
printf("C strlen: %d value: %sn",
strlen(missing_terminator.c_str()),
missing_terminator.c_str());
}
输出:
$ c++ example.cpp
$ ./a.out
C++ size: 13 value: hello
world
C strlen: 6 value: hello
C++ size: 3 value: nnn
C strlen: 6 value: nnna�