我在计算机系统上阅读Carnegie Mellon幻灯片进行测验。在slide第49页:
使用无符号计数
使用无符号as循环索引的正确方法unsigned i; for (i = cnt-2; i < cnt; i--) a[i] += a[i+1];
更好
size_t i; for (i = cnt-2; i < cnt; i--) a[i] += a[i+1];
我不明白为什么它不会是无限循环。我正在递减i
并且它是无符号的,因此应始终小于cnt
。请解释一下。
答案 0 :(得分:3)
这个循环只是依赖于i
将递减超过0的事实,这使得它成为最大值。由于现在i < cnt == false
,这打破了循环。
无符号数字不能溢出,而是使用 模数的属性。
C和C ++标准都保证了这种uint包装行为,但它对于有符号整数是未定义的。
答案 1 :(得分:2)
循环的目标是从cnt-2
循环到0
。它实现了写i >= 0
。
上一张幻灯片正确地说明了为什么i >= 0
的循环条件不起作用。无符号数始终大于或等于0
,因此这样的条件将是空的。 i < cnt
的循环条件最终循环,直到i
超过0
并且回绕。当您递减无符号0
时,它变为UINT_MAX
(2 32 - 对于32位整数为1)。当发生这种情况时,i < cnt
保证为假,并且循环终止。
我不会写这样的循环。这在技术上是正确的,但非常差的风格。好的代码不仅正确,而且可读,因此其他人可以很容易地弄清楚它在做什么。
答案 2 :(得分:2)
它正在利用递减无符号整数0时发生的情况。这是一个简单的例子。
unsigned cnt = 2;
for (int i = 0; i < 5; i++) {
printf("%u\n", cnt);
cnt--;
}
那产生......
2
1
0
4294967295
4294967294
无符号整数0 - 1变为UINT_MAX
。所以,不要寻找-1,而是要注意你的计数器何时变得大于其初始状态。
稍微简化一下这个例子,这里是你如何从5倒数到0(独家)。
unsigned i;
unsigned cnt = 5;
for (i = cnt-1; i < cnt; i--) {
printf("%d\n", i);
}
打印:
4
3
2
1
0
在最后一次迭代i = UINT_MAX
上保证大于cnt
,因此i < cnt
为假。
size_t
“更好”,因为它是无符号的,并且它与C中最大的东西一样大,因此您无需确保cnt
与i
的类型相同
答案 3 :(得分:1)
这似乎是实现相同事物的既定习语的另一种表达方式
for (unsigned i = N; i != -1; --i)
...;
他们只是将i != -1
更具可读性的条件替换为更隐蔽的i < cnt
。当0
在unsigned
域中递减时,它实际上会回绕到UINT_MAX
值,该值等于-1
(在无符号域中)并且大于或等于cnt
等于i != -1
。因此,i < cnt
或cnt - 2
可用作继续迭代的条件。
为什么他们会这样做呢?显然是因为它们从cnt
开始,而2
的值可能小于i != -1
,在这种情况下,它们的条件确实正常工作(并且cnt
没有)。除了这种情况之外,没有理由让cnt
参与终止条件。有人可能会说,更好的想法是预先检查i != -1
的值,然后使用if (cnt >= 2)
for (unsigned i = cnt - 2; i != -1; --i)
...;
成语
i
注意,顺便说一句,只要知道i != -1
的起始值是非负的,无论i
是否已签名,基于20670 * 395 = 8164650
条件的实施都会有效或未签名。
答案 4 :(得分:0)
我认为你对int和unsigned int数据类型感到困惑。这两个是不同的。在 int 数据类型(2字节存储大小)中,您的范围从 -32,768到32,767 ,而在 unsigned int 数据类型(2字节)存储大小)。你有 0到65,535 的范围。 在上面提到的示例中,您使用的是unsigned int类型的变量i。它将递减到i = 0,然后根据语义结束for循环。