添加调试输出(Heisenbug?)时,循环结果是不同的

时间:2019-06-25 06:50:29

标签: c++

如果在循环中添加调试输出,则我的循环的行为会有所不同。我想知道这是否是编译器错误,还是我错误地依赖于某些未定义的C++行为。

循环从uint64_t数组中读取整数,将它们存储在temp数组中,然后对各项求和。由于我有数据,我希望总和大于60。

如果它小于60(令人惊讶),我将重试同一循环,这次添加调试输出。我希望得到小于60的结果,但是现在大于60。

uint64_t test[8];
for(int i=0; i<8; i++) {
    test[i] = overflow[index + i];
}
int temp[64];
int count2 = 0;
for(int j = 0; j < 64; j++) {
    int cj = (int) ((test[j / 8] >> (8 * j)) & 0xff);
    count2 += cj;
    temp[j] = cj;
}
if (count2 < 60) {
    int count3 = 0;
    for(int j = 0; j < 64; j++) {
        int cj = (int) ((test[j / 8] >> (8 * j)) & 0xff);
        temp[j] = cj;
        ::std::cout << " foo\n";
        count3 += cj;
    }
    ::std::cout << "count2 " << count2 << " count3 " << count3 << "\n";
}

示例输出:

foo
...
foo
count2 6 count3 62

循环的方向无关紧要(如果循环从0到63,则结果相同)。只有一个线程。如果我在第二个循环中评论cout,例如“ count2 4 count3 4” (某些原始的意外情况)。一旦在循环中执行任何形式的cout(甚至是“ foo”),我就会再次得到count2!= count3。我试图使第一个循环更复杂(不必要将cj乘以100),但结果相同。

Makefile中的编译器选项:

OPT = -O3 -DNDEBUG -march=native
CXXFLAGS += -fno-strict-aliasing -Wall -std=c++11 $(OPT)
LDFLAGS = -Wall

与LLVM和g ++相同的结果。

"-O2"(g ++,未尝试LLVM)解决了问题。

1 个答案:

答案 0 :(得分:1)

在C ++(和C)中,未定义将k位整数在任何方向上移位超过k-1位。

因此,(overflow[index + j / 8] >> (8 * j))j >= 8时是未定义的,这(显然)会在您的两个循环中引起非常不同的行为。

我认为这应该会产生预期的结果:

int temp[64] = {0};
for(int j = 0; j < 8; j++) {
    int cj = (int) ((test[j / 8] >> (8 * j)) & 0xff);
    count2 += cj;
    temp[j] = cj;
}