【问题标题】:g++ 4.8.5 loop optimization bug with negative array indexes带有负数组索引的 g++ 4.8.5 循环优化错误
【发布时间】:2017-10-30 14:01:54
【问题描述】:

我怀疑我发现了一个 g++ 优化错误,该错误与取消引用具有负索引的对象(结构)中的数组有关。

在下面,节点是一个结构,它有一个数组前面它(在我的真实代码中,它是一个跳过列表的节点,它的指针数量和数据包的大小都是变量和底层 SkipList 代码未知,因此决定将指针放在对象引用之前和数据包之前 - 这里很长 - 在对象之后)。

#include <iostream>
#include <stdlib.h>

class Node {
public:
  unsigned int ptr[1]; // really an array going backwards
  long datum; // This seems to be necessary for the bug to surface                                                                                                                                       

};

class NodeList {
public:
  Node* hdr;
  NodeList() {
    void* p_v = malloc( sizeof(Node) + 32 * sizeof( unsigned int ) );
    hdr = (Node*)((char*)p_v + 32 * sizeof(unsigned int));
    hdr->ptr[-5]=100;
  }
  void setNodes() {
    int nn=0;
    while( rand() > 20 && nn<9 ) {
      nn++;
    }
    if( nn < 9 ) {
      nn = 9;
    }
    // It is a logical truth that nn = 9 here                                                                                                                                                            
    //nn = 9; // IF THIS IS UNCOMMENTED EVERYTHING WORKS!                                                                                                                                                
    std::cout << "nn=" << nn << " (should be 9) " << std::endl;
    int ctr = 0;
    for( int i=0; i<=nn; i++ ) {
       ctr++;
       hdr->ptr[-i]=0;
    }
    std::cout << "ctr was incremented " << ctr << " times (should be 10) and hdr->ptr[-5] = " << hdr->ptr[-5] << " (should be 0)\n";
  }
};

int main( int argc, char** argv ) {
    NodeList list;
    list.setNodes();
 }

预期输出 ctr 增加 10 倍, hdr->ptr[-5] 为 0。优化后的代码只循环一次(即不循环),并将 ptr->hdr[-5] 保留为 100 . 这是一个错误。

-fno-aggressive-loop-optimizations 似乎可以修复它,但如果输出代码正确显然会更好。

我把它放在这里是为了 (a) 验证这是一个错误,因为我是这里的新手,这是我的第一个问题,(b) 询问 gcc 开发社区中的任何知识渊博的人该怎么做(例如,我应该如何报告它,以及它是否已在以后的版本中修复),以及 (c) 允许在 CentOS 7(或任何其他 4.8 发行版)上经历过这个最令人沮丧和耗时的问题的人查看确认他们遇到了来自其他患者的错误!

【问题讨论】:

  • 对(C 风格)数组使用负索引不是一种未定义的行为吗?
  • 不,arr[n] 被定义为*(arr+n) 用于所有 n(正数和负数)。当然,要确保引用是正确分配的内存,这取决于实现。见this question
  • this question 可能有您想要的答案。
  • 只是普通索引超出范围,它是 UB。数组被声明为具有1 的大小,因此除 [0] 之外的任何数组索引都是未定义的行为。我怀疑您可能试图将已定义的 C 行为与未定义的 C++ 行为混合在一起。请参阅en.cppreference.com/w/cpp/language/ub中的越界访问
  • @RichardCritten 请将此评论作为答案。

标签: c++ arrays loops optimization g++


【解决方案1】:

这里必须小心“数组”的含义。数组是任何定义为

some_type arr[number];

但不是类似

some_type*ptr = some_address;

负索引与正索引没有什么不同,ptr[n] 被解释为*(ptr+n)(你甚至可以在 C 中使用n[ptr])。因此,在索引时,使用指针算法。

在 C 和 C++ 中未定义的行为 (UB) 访问数组边界之外的元素,无论这是如何实现的。例如

some_type arr[10];
some_type*ptr = arr+5;
some_type foo = ptr[-4];    // ok, access to arr[1]
some_type bar = ptr[-6];    // UB, out-of-bound access
some_type val = arr[-1];    // UB, out-of-bound access

【讨论】:

  • 在某处声明类似some_type var1; 的东西怎么样。然后某处稍后some_type* var_p = &amp;var1; 现在我的问题是引用说var_p[1]var_p[-1] 是否是UB?如果是这样,您将如何在不调用 UB 的情况下在对象之前引用数组?
  • @galois99 这与您的原始帖子完全不同,因为不涉及数组。我不确定,但强烈建议不要做这种事情,除非你非常清楚自己在做什么(你不知道,否则你不会问)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-06
  • 2018-11-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-20
  • 2012-03-24
相关资源
最近更新 更多