【问题标题】:for (unsigned char i = 0; i<=0xff; i++) produces infinite loopfor (unsigned char i = 0; i<=0xff; i++) 产生无限循环
【发布时间】:2018-01-11 14:29:58
【问题描述】:

为什么下面的c代码会陷入死循环?

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

结果相同:

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

我必须以何种方式修改代码,以使其按预期工作(不使用intunsigned int 数据类型)?

【问题讨论】:

  • 你明白什么是无符号数吗?
  • 8位unsigned char不可能存储大于0xff的值,因此i &lt;= 0xff不可能是false
  • 你的两个例子是相同的。我会假设他们有相同的结果。
  • 使用int。不要对可接受的答案施加愚蠢的限制。
  • 请注意,如果您使用-Wextra 进行编译,gcc 会为您的循环发出警告,说明由于数据类型限制,条件始终为真。

标签: c loops unsigned-char


【解决方案1】:

因为你环绕。 unsigned char 值的范围在 0-255 之间,并且由于 unsigned 算术是很好定义的,因此您实际上将 i 的值包装为 0 并且条件仍然满足并且迭代无限地继续。

在有符号值的情况下,这是一个未定义的行为,存储在 i 中的值可能不是 0。但它仍然会小于您可以存储在 char 中的最大值并且仍然满足条件。

【讨论】:

  • 不,溢出的无符号整数是明确定义的;它们包含在模算术之下。
  • unsigned 值不会溢出,它会换行。 signed 值会溢出。
  • 不存在加法或算术“溢出”问题。不过也有类似的问题,将 256 分配给 unsigned char。同样的结果,i 为 0。
  • “在有符号值的情况下”不适用于此处。该值从 0 到 255 到 256(暂时)再到 0... 没有带符号的值 - 它们都是非负数。如果您正在考虑 i 的签名类型,则如果 iint 或 sub-int,答案会有所不同。一个是 UB,另一个是 IDB/异常。
  • @chux 只是针对未定义的行为说的,所以如果他使用 Wea​​therVane 提出的出色解决方案,他需要知道有符号值可能不会包装为 0 并且 do-while 不会按预期工作。
【解决方案2】:

unsigned char 的范围是 0 到 255 (0xff)。将 1 加到 0xff 会得到 0x100,当分配回 unsigned char 时会回绕回 0。

所以i &lt;= 0xff 的比较总是正确的。因此是无限循环。

如果您希望循环在 0xff 之后停止,请使用 int 作为数据类型。这是一个至少 -32767 到 32767 的范围,通常更多。

【讨论】:

  • 嗯,也许 OP 应该从问题中删除 (不使用 intunsigned int 数据类型)
  • "无符号字符的范围是 0 到 255 (0xff)。"在某些平台上。
  • Nitpick:int 的最小要求范围实际上是 -3276732767(以允许补码机器)
  • 详细信息:“将 1 加到 0xff 将回绕回 0”--> 将 1 加到 0xFF 将得到 256,int。正是将 256 分配回 unsigned char 处理 like 无符号“溢出”,并由于分配转换将 0 分配给 i
  • @chux 好点。编辑以反映并修复int 的最小范围。
【解决方案3】:

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

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

当对unsigned 整数的算术运算突破其限制时,行为是明确定义的。所以这个解决方案将处理 256 个值(对于 8 位 unsigned char)。

【讨论】:

  • 我该死,do while 循环的另一种罕见用法。
  • @StoryTeller 一样!我一直认为 do-while 循环仅用于定义多行宏!
  • @StoryTeller 另一个do-while 用法是在循环中将整数转换为字符串时,当输入为0 时需要输出"0",但在value /= 10 时需要终止变成 0.
【解决方案4】:

典型的for 循环依赖于在循环的最后一次迭代之后检测终止条件。在您的情况下,正如其他答案所指出的那样,i &lt;= 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 &gt; 8,因此UCHAR_MAX &gt; 255.)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-14
    • 2022-01-05
    • 2016-03-18
    • 1970-01-01
    • 1970-01-01
    • 2011-07-27
    • 1970-01-01
    相关资源
    最近更新 更多