15

我对 Stackoverflow 进行了一些关于 C++ 中使用无符号整数而不是有符号整数的反向循环的研究。但我仍然不明白为什么会出现问题(请参阅Unsigned int reverse iteration with for loops)。为什么下面的代码会产生分段错误?

#include <vector>
#include <iostream>
using namespace std;

int main(void)
{
    vector<double> x(10);

    for (unsigned int i = 9; i >= 0; i--)
    {
        cout << "i= " << i << endl;
        x[i] = 1.0;
    }

    cout << "x0= " << x[0] << endl;

    return 0;
}

我知道问题是什么时候索引 i 将等于零,因为有类似溢出的东西。但我认为允许无符号整数取零值,不是吗?现在如果我用有符号整数替换它,绝对没有问题。

有人可以用无符号整数向我解释那个反向循环背后的机制吗?

非常感谢你!

4

5 回答 5

31

这里的问题是无符号整数永远不会是负数。

因此,循环测试:

i >= 0

永远都是真的。因此你得到一个无限循环。

当它低于零时,它会回绕到unsigned最大值。
因此,您还将访问x[i]out-of-bounds

这对于有符号整数来说不是问题,因为它只会变成负数并因此失败i >= 0

因此,如果您想使用无符号整数,您可以尝试以下可能性之一:

for (unsigned int i = 9; i-- != 0; )

for (unsigned int i = 9; i != -1; i--)

这两个是 GManNickG 和 AndreyT 从评论中建议的。


这是我原来的 3 个版本:

for (unsigned int i = 9; i != (unsigned)0 - 1; i--)

或者

for (unsigned int i = 9; i != ~(unsigned)0; i--)

或者

for (unsigned int i = 9; i != UINT_MAX; i--)
于 2012-01-28T08:57:18.937 回答
6

问题是,您的循环允许 i 低至零,并且仅在 i 小于 0 时才期望退出循环。由于 i 是无符号的,它永远不会小于 0。它会翻转到 2^32-1 . 这大于向量的大小,因此会导致段错误。

于 2012-01-28T08:58:59.910 回答
4

无论它的价值是什么,unsigned int i它总是如此,i >= 0所以你的for循环永远不会结束。

换句话说,如果在某个点i为 0 并且您将其递减,它仍然保持非负数,因为它包含一个巨大的数字,可能是 4294967295(即 2 32 -1)。

于 2012-01-28T08:58:33.983 回答
3

问题在这里:

for (unsigned int i = 9; i >= 0; i--) 

您从 unsigned int 的值 9 开始,您的退出定义是 i >= 0,这将始终是正确的。(unsigned int 永远不会是负数!!!)。因此,您的循环将重新开始(无限循环,因为 i=0 然后 -1 变为最大 uint)。

于 2012-01-28T09:02:52.997 回答
0

正如您所说,在循环的最后一步之后发生的无符号数减少到零以下会产生溢出,该数字会回绕到其最大值,因此我们最终会出现无限循环。

有人可以用无符号整数向我解释那个反向循环背后的机制吗?

对于带有索引的反向循环,我首选的方法是:

for (unsigned int i = 9; i > 0; --i) {
    cout << "i= " << x[i - 1] << endl;
}

这就是为什么因为它最接近地映射到正常循环等效项:

for (unsigned int i = 0; i < 9; ++i) {
    cout << "i= " << x[i] << endl;
}

如果您需要多次访问索引元素并且不想连续写入 [i - 1],则可以在循环的第一行添加类似这样的内容:

auto& my_element = my_vector[i - 1];
于 2021-01-22T17:12:05.443 回答