将 std::string 始终以 C++11 为空结尾

Will std::string always be null-terminated in C++11?

本文关键字:C++11 结尾 std string      更新时间:2023-10-16

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)

这意味着给定一个字符串ss.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下引起数据争用;我不同意这将是一场数据竞赛。

尽管 c_str() 返回 std::string

的 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�