【问题标题】:Will C++11 std::string::operator[] return null-terminated bufferC++11 std::string::operator[] 会返回空终止缓冲区吗
【发布时间】:2016-12-16 21:01:03
【问题描述】:

我有一个 std::string 类的对象,我需要将它传递给 C 函数,该 C 函数通过迭代它并搜索空终止符号来操作 char* 缓冲区。

所以,我有这样的事情:

// C function
void foo(char* buf);

// C++ code
std::string str("str");
foo(&str[0]);

假设我们使用 C++11,所以我们保证std::string 表示将具有连续存储的字符。

但我想知道是否有任何保证&str[0] 将指向以\0 结尾的缓冲区?是的,有c_str 成员函数,但我说的是operator[]

有人可以引用标准吗?

【问题讨论】:

  • 是的,但str.data()/str.c_str() 更惯用。
  • @Mohit Jain 因为该函数接受char*,而不是const char*
  • @FrozenHeart 我不是 100%,但我认为修改 std::string 的缓冲区可能是 UB(请参阅 @Zereges 答案中的 Requires )。
  • @Holt 好吧,我不能修改那个函数

标签: c++ c++11


【解决方案1】:

在实践中,是的。 std::string 的完全零个实现符合标准,不会在缓冲区末尾存储 NUL 字符。

因此,如果您不是为了好奇而好奇,那么您就完成了。

但是,如果您想知道标准是否深奥:


在 C++14 中,是的。有一个明确的要求,[] 返回一组连续的元素,[size()] 必须返回一个 NUL 字符,并且 const 方法不能修改状态。所以*((&str[0])+size())必须和str[size()]一样,str[size()]必须是NUL,游戏就结束了。


几乎可以肯定,在 C++11 中。有规则const 方法不能修改状态。保证data()c_str() 在每一点都返回一个与[] 一致的空终止缓冲区。

对 C++11 标准的复杂解读表明,在调用 data()c_str() 之前,[size()] 不会在缓冲区末尾返回 NUL 终止符,而是返回 static const CharT它是单独存储的,并且缓冲区在 NUL 应该是一个未初始化的(甚至是一个陷阱值)。由于const 方法不修改状态的要求,我认为这个读数是不正确的。

这需要在对.data() 的调用之间更改&str[str.size()],这是stringconst 调用上的可观察到的状态变化,我认为这是非法的。

绕过标准的另一种方法可能是不初始化str[str.size()],直到您通过调用.data().c_str() 或实际将str.size() 传递给operator[] 合法访问它。由于除了标准中的 3 种之外,没有定义访问该元素的方法,因此您可以扩展内容并说 NUL 的延迟初始化是合法的。

我会质疑这一点,因为.data() 的定义意味着[] 的返回值是连续的,所以&[0].data() 的地址相同,并且.data()+.size() 保证指向一个 NUL CharT 所以必须 (&[0])+.size(),并且没有非 const 方法调用,std::string 的状态可能不会在调用之间改变。

但是,如果编译器可以看到你永远不会调用.data().c_str(),如果可以证明你永远不会调用它们,那么连续性要求是否成立?

此时我会举起双手向敌对编译器开枪。


标准对此非常被动地表达。因此,可能有一种方法可以制定符合std::string 的标准,但不遵循这些规则。而且由于保证越来越接近明确要求在那里使用 NUL 终止符,因此出现新编译器的可能性很低,该编译器使用对 C++ 的痛苦解读来声称这是符合标准的。

【讨论】:

  • 我认为这属于奥卡姆的实现:最简单的实现是最有可能的,除非另有证明。
  • @Mgetz 是的,但我们也想确保没有病态的编译器出现并说“那是 UB,所以我会围绕它进行优化”。 咳嗽 gcc 咳嗽喉咙里有东西。
  • 此时我会举手向敌对编译器开枪。 还要将其火化并撒灰,这样它就不会再打扰任何人了。
  • “明确要求 [] 返回一组连续的元素,并且 [size()] 必须返回 NUL 字符”——你能引用它吗?事实上,我看不出 C++11 和 C++14 草案中 operator[] 的描述有什么区别
【解决方案2】:

按照标准,是的。底层 char 容器可以使用 string::datastring::c_str 访问,标准上说:

21.4.7.1 basic_string访问器[string.accessors]
const charT* c_str() const noexcept;
const charT* data() const noexcept;

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

为了证明它是空终止的,请查看operator[] 的定义(强调我的):

21.4.5 basic_string元素访问[string.access]
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);

1 要求: pos <= size().
2 返回: *(begin() + pos) 如果pos < size()否则,返回对类型为 charT 且值为 charT() 的对象的引用,其中修改对象会导致未定义的行为。
3 投掷:没有。
4 复杂性:恒定时间。

因此operator[size()] 返回charT(),并且由于std::stringstd::basic_string<char>charT()'\0'

这意味着,在您的情况下,*(&str[0] + str.size()) == '\0' 按照标准应始终为true


注意,修改operator[size()] 是UB。

【讨论】:

  • 它在哪里声明它应该是空终止的?
  • 没错。我正在寻找这个。我很久以前就读过这篇文章。
  • 第 3 点是否意味着您不应该使用 this 将该指针传递给接受 char* 的函数?
  • @FrozenHeart:从逻辑上一步一步地思考:1)c_str() 必须以空值结尾(在规范的其他地方提到)....2)并且p+i == &operator[](i) 是有效的对于[0, size()] 范围内的所有i,表示i==size()\0 时的最后一个字符的地址。
  • @FrozenHeart 也许 foo 应该是 void foo(char const * buf); 然后
猜你喜欢
  • 2016-08-29
  • 2015-02-27
  • 2015-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-30
  • 2019-10-18
相关资源
最近更新 更多