为什么std :: vector :: at()即使启用了优化也需要边界检查?

时间:2016-11-16 12:01:20

标签: c++ windows optimization dll indexoutofboundsexception

我在Windows中遇到libstdc++-6.dll的问题。这段代码:

#include <iostream>
#include <vector>
int main(){
    std::vector<int> x(10);
    std::cout << x.at(3) << std::endl;
}

编译好,但是当我运行它时,我收到一条错误消息

  

程序入口点_ZSt24__throw_out_of_range_fmtPKcz无法在dll libstdc ++中找到 - 6.dll

我的问题是如何解决这个问题(很可能是dll的错误版本,我只需修复PATH)。然而,这让我意识到一些非常意外的事情:

当我启用优化时,上面的代码运行良好(无论错误的dll如何),即

g++ error.cxx -O2

但是这段代码

#include <vector>
#include <iostream>

double Foo(const std::vector<int>& x,int index) {    
    int m = x.at(index + 1) - x.at(index);
    int b = x.at(index);
    return b/(m*1.0);
}        
int main(){}

没有。

为什么我用第二个代码得到上面提到的错误,无论我是通过

编译它
g++ error.cxx -O2     or       g++ error.cxx 

同样,我知道为什么会出现错误,但我希望在启用优化的情况下,两个版本都不会导致错误。相反,第一个版本工作正常,而第二个版本没有。 -O2不应该完全消除边界检查吗?

3 个答案:

答案 0 :(得分:10)

C ++标准要求at()边界检查,at()的实际实现代码确实包含边界检查。

然而,在你的第一种情况下,边界是硬编码的(10个元素对比索引3)和&#34;所有东西&#34;内联-O2,因此编译器的优化器会删除边界检查违规的代码,因为它可以在编译时证明边界没有被违反而代码路径没有采取(如果规则)。

因此,在这种情况下,你没有得到-O2链接器错误,因为编译器根本就没有发出调用指令。

  

不应该-O2完全消除边界检查吗?

不,优化器必须保持the AS-IF rule,也就是说,如果优化器在编译时可以证明没有采用代码路径,那么它可以消除该代码。它不会毫不犹豫地删除源代码中引入的检查。

作为旁注,对于要求边界检查的vector::operator[],实现可以(合理地或不合理地)引入调试边界检查,例如, NDEBUG未定义,仅在定义NDEBUG时不进行检查。但是,在这种情况下,边界检查将被预处理器&#34;删除。如果你愿意,而不是优化者。

答案 1 :(得分:2)

C ++标准要求绑定检查std::vector::at

  

23.2.3序列容器

     
      
  1. 序列容器将所有相同类型的有限对象组织成严格的线性排列。该库提供了四种基本类型的序列容器:vector,...
  2.         

    ...

         
        
    1. 成员函数at()提供对容器元素的边界检查访问。如果at() {/ 1}},out_of_range会引发n >= a.size()   

答案 2 :(得分:1)

如果索引超出范围,则指定

std::vector::at抛出异常。如果您不想要边界检查,请使用operator[]

m.at(index);
m[index]; //change to this

您的第一个示例不会抛出异常,因为std::vector<int> x(10);会创建一个值为int的{​​{1}}的向量,而不是一个值为0的向量{ {1}}。

int