用无符号计数的正确方法

时间:2017-02-26 05:57:48

标签: c loops unsigned

我在计算机系统上阅读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。请解释一下。

5 个答案:

答案 0 :(得分:3)

这个循环只是依赖于i将递减超过0的事实,这使得它成为最大值。由于现在i < cnt == false,这打破了循环。

Overflowing of Unsigned Int

  

无符号数字不能溢出,而是使用   模数的属性。

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中最大的东西一样大,因此您无需确保cnti的类型相同

答案 3 :(得分:1)

这似乎是实现相同事物的既定习语的另一种表达方式

for (unsigned i = N; i != -1; --i) 
   ...;

他们只是将i != -1更具可读性的条件替换为更隐蔽的i < cnt。当0unsigned域中递减时,它实际上会回绕到UINT_MAX值,该值等于-1(在无符号域中)并且大于或等于cnt等于i != -1。因此,i < cntcnt - 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循环。