对于 (unsigned char i = 0; i<=0xff; i++),会产生无限循环。

4
为什么以下C代码会陷入无限循环?
for(unsigned char i = 0; i <= 0xff; i++){}

这与以下结果相同:

for(unsigned char i = 0; i <= 0xff; ++i){}

我需要修改代码的哪些部分才能使其按预期工作(不使用 intunsigned int 数据类型)?


1
你了解无符号数是什么吗? - NathanOliver
14
一个8位的unsigned char最大只能存储0xff这个值,因此,i <= 0xff不可能为假。 - François Andrieux
1
你的两个例子是相同的。我会认为它们有相同的结果。 - François Andrieux
1
使用 int。不要对可接受的答案设置愚蠢的限制。 - Benjamin Lindley
1
请注意,如果您使用了“-Wextra”编译选项,gcc会为您的循环发出警告,解释由于数据类型限制,条件始终为真。 - spectras
显示剩余3条评论
4个回答

18

如果您真的需要使用 unsigned char,那么您可以使用

unsigned char i = 0;
do {
    // ... 
} while(++i);

当对一个unsigned整数执行算术运算超出其限制时,其行为是定义良好的。因此,该解决方案将处理256个值(对于8位unsigned char)。


8
该死,又一个罕见的do-while循环的使用。 - StoryTeller - Unslander Monica
1
@StoryTeller 同感!我一直以为 do-while 循环只用于定义多行宏! - Ajay Brahmakshatriya
1
@StoryTeller 另一个使用 do-while 的例子是在循环中将整数转换为字符串,当输入为 0 时需要输出 "0",但需要在 value /= 10 变为 0 时终止。 - Weather Vane

8

unsigned char 的取值范围是0到255(0xff)。将0xff加1得到0x100,但是如果将它再赋值回 unsigned char ,它会被截断并变成0。

因此比较语句 i <= 0xff 将永远为真。这就是为什么会产生无限循环的原因。

如果你想让循环在0xff后停止,请使用 int 数据类型。它的取值范围至少是-32767到32767,并且通常更大。


1
挑剔一点:int 的最小要求范围实际上是 -3276732767(以允许使用补码机器)。 - Andrew Sun
1
@chux 说得好。已经修改以反映,并修复了 int 的最小范围。 - dbush
@Slava 在一些平台上是这样的吗?现在只有一些专门的老式DSP处理器才会出现这种情况。 - 0___________
@Andrew Sun,允许使用补码机器的话,有没有比30岁更年轻的?并且符号和大小比50岁更年轻的呢? - 0___________
@Slava,你有没有使用过字符不等于8位的机器?我个人没有,我已经从事这项工作30多年了。但是,如果我必须为DSP处理器编写固件,代码的可移植性将是最不重要的问题。 - 0___________
显示剩余7条评论

5
一个典型的for循环依赖于能够在循环的最后一次迭代之后检测到终止条件。在您的情况下,正如其他答案所指出的那样,i <= 0xff始终为真(假设i是unsigned char类型且UCHAR_MAX==0xff,这是典型情况)。
在任何整数类型的边界附近都可能遇到同样的问题。例如,以下代码:
for (int i = INT_MAX - 9; i <= INT_MAX; i ++) {
    /* ... */
}

这(可能)也是一个无限循环(除了对于有符号整数类型的溢出具有未定义行为,而无符号整数具有明确定义的环绕语义,优化编译器可以利用它,但我偏离了主题)。只是不要这样做。

许多解决方案之一是将测试移动到循环的底部,在增量之前:

for (unsigned char i = 0; ; i ++) {
    printf("%d\n", i);
    if (i == 0xff) break;
}
for循环的第二个语句是每次迭代之前都会被评估的终止条件。如果您将其留空,则会被视为始终为真,从而产生无限循环(for (;;)是简单无限循环的常见习惯用法)。在循环的底部,我们测试i是否等于0xff。如果是,则我们刚刚执行了最后一次迭代,因此可以跳出循环。
(有些人可能更喜欢在这里使用while循环,但我喜欢for,因为它允许我们将循环控制变量的声明和增量组合成一个结构。)
(严格来说,unsigned char的最大值不一定是0xff255,但在您可能遇到的任何系统中都是如此。一些DSP的实现具有CHAR_BIT > 8,因此UCHAR_MAX > 255。)

2
因为你进行了环绕。unsigned char的取值范围在0-255之间,由于无符号算术是被很好定义的,因此当将i的值环绕到0时条件仍然满足,迭代将无限继续。
对于有符号值,这是一种未定义的行为,存储在i中的值可能不是0。但它仍然小于char中可存储的最大值,并且条件仍然得到满足。

4
不,无符号整数溢出是被很好地定义了的;它们在模运算下会被包裹回来。 - Justin
4
无符号数值不会溢出,而是会环绕。有符号数值则会发生溢出。 - Weather Vane
没有加法或算术“溢出”问题。然而,存在一个类似的问题,即将256分配给“unsigned char”。结果相同,i为0。 - chux - Reinstate Monica
1
这里不适用“在有符号值的情况下”。该值从0到255再到256(短暂地),然后回到0...没有有符号值-它们都是非负数。如果您考虑对i使用有符号类型,则答案取决于iint还是子int。一个是UB,另一个是IDB/异常。 - chux - Reinstate Monica
@chux 这只是针对未定义行为而言的,因此如果他使用了WeatherVane提出的好方法,他需要知道有符号值可能不会回绕到0,do-while循环也无法按预期工作。 - Tony

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接