【问题标题】:Null terminated string, is it really dictated by the standard? [duplicate]空终止字符串,它真的由标准规定吗? [复制]
【发布时间】:2014-07-31 20:47:29
【问题描述】:

讨论

  • 已知从 C++11 及以后的std::basic_strings 被认为具有空字符终止的内部存储缓冲区。

  • 除其他外,此更改的主要原因是 std::basic_string 的先前定义仅允许对字符串进行非常有限的并发访问,因此限制了多线程应用程序的性能。 (更多std::basic_string变化的原因可以阅读proposal N2534)。

  • 但是,阅读标准时,我找不到明确指出 std::basic_string 必须有一个以空字符结尾的内部存储缓冲区的引用。

  • 我发现的唯一隐式引用是§21.4.7.1/1&3 basic_string accessors [string.accessors]:

const charT* c_str() const noexcept;

const charT* data() const noexcept;

1 返回:一个指针p,使得p + i == &operator[](i) 对应[0,size()] 中的每个i 3 要求:程序不得更改存储在字符数组中的任何值。

  • 我假设由于效率原因,并且由于 §21.4.7.1/3 要求程序不得更改返回的缓冲区,std::basic_string::c_str()std::basic_string::data() 中的大多数实现者都返回以空字符结尾的内部缓冲区。

  • 1234563 /p>

问题:

  1. 标准中是否有明确规定std::basic_string内部存储缓冲区必须以空字符结尾?
  2. 如果没有明确的声明(即,问题 #1 的简短回答是否定的),这是否意味着实施者可以在没有空字符终止的内部存储缓冲区的情况下实施 std::basic_string,从而广泛传播概念既然 C++11 字符串是空终止的,那是错误的吗?

【问题讨论】:

  • 部分原因也在于它们的复杂性。
  • 我还隐约记得至少有一个std::string 操作可以显式取消引用但不能提前end 迭代器。
  • 阅读副本的答案后,我得出的结论是,没有针对空终止缓冲区的显式指令,但这是隐式强制的。还是我错了?

标签: c++ c++11 language-lawyer stdstring c++14


【解决方案1】:

21.4.5

const_reference operator[](size_type pos) const;reference operator[](size_type pos);

1 要求:pos <= size()

2 返回: *(begin() + pos) 如果pos < size(),否则引用 T 类型的对象,值为 charT();参考值应 不可修改。

请注意,s[s.size()] 定义明确,需要返回 NUL 字符。

但是,这本身并不需要NUL-终止的内部存储。 21.4.1/5 有这样的说法:

basic_string 对象中的类字符对象应连续存储。也就是说,对于任何basic_string 对象s,身份&*(s.begin() + n) == &*s.begin() + n 应适用于n 的所有值,使得0 <= n < s.size()

请注意,仅n < s.size() 之前需要连续存储,s.size() 本身不需要。所以char* p = &s[0]; 不一定指向NUL 终止的缓冲区,因为标准不要求p[s.size()] 有效。

实际上,在data()c_str()operator[] 的要求之间,任何合理的实现都会维护NUL 终止的存储。但标准似乎并未排除疯狂的实现。

【讨论】:

  • 但是该引用并不要求内部存储本身以空值结尾,只是它要求[] 在请求< size() 时返回对内部存储的引用,并且对a 的引用否则为空字符。这并不要求 null 来自内部存储,它可以来自内部常量文字(这是 c_str() 通常在 size() 为 0 时返回指向的指针)。在这个引用中,没有什么要求s[s.size()-1]s[s.size()] 在内存中返回引用连续数据。
  • @RemyLebeau:相当。实际上,我在下半场就在写这个。
  • 我突然想到,无论你怎么剪,这都是一个棘手的要求,因为许多string 实现没有为默认构造分配内存。由于std::string{}[0] 需要返回一个可读的引用,所以无论如何实现都必须对end 进行特殊处理。
  • 如果s.size() 为0,s[0] 返回一个空字符的引用,所以p[0] 是有效的。如果s.size() >0,s[0] 返回对内部存储开头的引用,p[s.size()] 仅在内部存储为空终止时有效。
【解决方案2】:

在 C++11 中,c_str()data() 都需要返回指向内部存储的指针(抱歉,我没有方便的直接引用来支持它)。在早期版本中,c_str() 不需要这样做,但 data() 是。实现者可以(但很少这样做)实现写时复制语义,以便c_str() 返回的指针不是原始内部存储。

在所有版本中,c_str() 都需要返回一个以空值结尾的指针。因此,这意味着在 C++11 中,内部存储必须以空值结尾。

【讨论】:

  • 没有什么可以阻止c_str() 在它返回之前放入空值(但是,它必须已经有空间)。
  • data() 存在于C++03,什么版本不存在?
  • @Nevin 哦,原来如此。我以为它是 C++11 的新手,但我错了。
  • @MooingDuck,你可能一直在想vector,直到C++11才得到它。
  • @MooingDuck:如果内部存储足够大,c_str() 可以在返回指向存储的指针之前在末尾插入一个空值。否则,它必须制作存储数据的副本,然后返回指向该副本的指针。在早期版本中,这是允许的。在 C++11 中,不允许重定位(即复制)数据,这意味着内部存储必须足够大以始终保存空值,即使在调用 c_str() 之前它实际上没有被插入。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-28
  • 1970-01-01
  • 2015-04-27
  • 2010-12-07
  • 2011-06-04
  • 2011-11-05
相关资源
最近更新 更多