在 C++11 及更高版本中,std::string::operator[] 是不是进行边界检查?

Posted

技术标签:

【中文标题】在 C++11 及更高版本中,std::string::operator[] 是不是进行边界检查?【英文标题】:In C++11 and beyond does std::string::operator[] do bounds checking?在 C++11 及更高版本中,std::string::operator[] 是否进行边界检查? 【发布时间】:2016-11-25 04:17:00 【问题描述】:

我多次看到std::string::operator[] 不做任何边界检查。甚至What is the difference between string::at and string::operator[]?,在 2013 年被问到,答案说operator[] 不做任何边界检查。

我的问题是,如果我查看 [string.access] 中的标准(在本例中为 draft N3797),我们有

const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
    要求: pos <= size()返回: *(begin() + pos) 如果pos < size()。否则,返回对类型为 charT 且值为 charT() 的对象的引用,其中修改对象会导致未定义的行为。 抛出:什么都没有。 复杂性: 恒定时间。

这让我相信operator[] 必须进行某种边界检查以确定它是否需要返回字符串的元素或默认的charT。这个假设是否正确并且现在需要operator[] 进行边界检查?

【问题讨论】:

std::basic_string::operator[] 不仅不做边界检查,它不能也不能做边界检查以符合要求。标准明确规定此函数不会抛出任何东西 @DavidHammen 你必须扔东西来做边界检查吗?一个符合要求的实现不能检查 pos >= size() 并返回 charT() 如果它愿意吗? @DavidHammen: exit() 不会抛出并且看起来符合标准。 @DavidHammen 要求 pos <= size() 表示如果 pos > size() 行为未定义。所以它仍然可以合法地扔东西。 【参考方案1】:

措辞有些混乱,但如果你仔细研究它,你会发现它实际上非常精确。

上面写着:

前提条件是[]的参数要么是=n,要么是n。 假设满足前提条件: 如果是 n,那么你会得到你想要的字符。 “否则”(即如果它是 n)那么您会得到 charT()(即空字符)。

但是当你打破前提条件时没有定义规则,可以通过实际存储 @ 来隐式满足 = n 的检查(但没有明确要求) 987654323@ 在位置 n

因此实现不需要执行任何边界检查……而普通的则不需要。

【讨论】:

“但是当你打破前提条件时没有定义规则”可能是你答案中最重要的部分;) 我被另一个前提条件绊倒了。我需要记住打破那些是UB,不管它的其余部分是什么。谢谢。 也许,有趣的是要记住 c_str 和 data() 自 c++11 以来执行相同的功能并且它们不会使迭代器无效。这意味着 charT() 存储在位置 n。 @AntonioGarrido 我猜一个神秘的实现仍然没有必要。据我了解,它仍然可以检查n == pos 情况并返回对静态charT 实例的引用,data()c_str 可以分配实习生状态的副本加上默认常量。 charT 最后。不可行,但恕我直言,允许。 @LightnessRacesinOrbit 我通常知道,但是第 1 行中 N4140 的第 21.4.7.1 节: -) 或多引用一点 【参考方案2】:

operator[] 进行了某种边界检查以确定...

不,它没有。有前提的

要求:pos

它可以假设它总是可以返回字符串的一个元素。如果不满足此条件:未定义的行为。

operator[] 可能只是将指针从字符串的开头递增 pos。如果字符串更短,那么它只会返回对字符串后面数据的引用,无论它可能是什么。就像简单 C 数组中的经典越界。

为了完善pos == size() 的情况,它可以在其内部字符串数据的末尾分配一个额外的charT。因此,仅在不进行任何检查的情况下增加指针,仍会提供所述行为。

【讨论】:

似乎没有回答这个问题,因为pos == size()是被问及的情况,与UB无关! 是的,忘记添加了。你的答案比我的好,(我会 +1ned),但也许多种风格的答案会有所帮助。【参考方案3】:

首先,有一个 requires 子句。如果您违反了 requires 子句,您的程序将以未定义的方式运行。那是pos <= size()

所以语言只定义了在这种情况下会发生什么。

下一段指出,对于pos < size(),它返回对字符串中元素的引用。对于pos == size(),它返回对默认构造的charT 的引用,其值为charT()

虽然这可能看起来像边界检查,但实际上实际发生的是std::basic_string 分配的缓冲区比请求的大一并且用charT() 填充最后一个条目。然后[] 只是做指针算术。

我试图想出一种方法来避免这种实施。虽然标准没有强制要求,但我无法说服自己存在替代方案。 .data() 有一些令人讨厌的地方,这使得很难避免使用单个缓冲区。

【讨论】:

【参考方案4】:

这个标准容器的操作符模拟普通数组的操作符 [] 的行为。所以它不做任何检查。但是在调试模式下,相应的库可以提供这种检查。

如果要检查索引,请改用成员函数at()

【讨论】:

【参考方案5】:

http://en.cppreference.com/w/cpp/string/basic_string/operator_at

返回对指定位置 pos 的字符的引用。 没有 执行边界检查。

(强调我的)。

如果你想要边界检查,使用std::basic_string::at

标准意味着实现需要提供边界检查,因为它基本上描述了未经检查的数组访问的作用。

如果您在范围内访问,则已定义。如果你走出去,你会触发未定义的行为。

【讨论】:

引用非规范网站违反标准不是证据。 @Yakk 引用一个备受推崇的参考网站,该网站无疑已尽其应尽的努力确保所说的内容符合标准并不是证明,但它是一个非常强烈的暗示,即推理我在后半部分提供的答案是正确的。 该标准并没有暗示任何边界检查的必要性,相反,它非常清楚地表明实现者只需要为 n

以上是关于在 C++11 及更高版本中,std::string::operator[] 是不是进行边界检查?的主要内容,如果未能解决你的问题,请参考以下文章

为啥支持 C++11 及更高版本的 C++ 编译器需要 Boost.SmartPtr?

在 Java 11 及更高版本中使用 HttpClient 时如何跟踪 HTTP 303 状态代码?

sh 在Mac 10.11.5及更高版本的10.11系列和10.12.1及更高版本的10.12系列中安装Caffe

AVKit.AVPlayerViewController - 控件在 iOS11 及更高版本中不可见

在 SQL Plus 中,当在 Oracle 11g 及更高版本中使用 lpad 时,第二列会获得额外的尾随空格

为啥 SSMS 17.x 及更高版本不支持 SSIS