【问题标题】:C for loop behaving strangelyC for 循环行为异常
【发布时间】:2026-01-29 06:10:02
【问题描述】:

我偶然发现了一个持续存在的问题,似乎没有合理的解释。问题似乎出在for (i = size - 1; i >= 0; i--) {etc.} 的for 循环中,其中size 是存储在内存缓冲区中的文件的大小,i 是一个无符号整数。它不是在i == 0 时停止,而是环绕 - 从而导致i = 4294967295 并导致分段错误。将条件更改为i > 0 即可解决问题。

不过,这样不是很奇葩吗?我一定遗漏了 for 循环如何在 C 中运行的一些关键部分。它不遵循这个方案:初始化、检查条件、递增/递减、检查条件等等?

感谢任何帮助!

【问题讨论】:

  • "i" 总是大于 0,因为 "i" 是一个无符号整数
  • 几乎但不完全是@RaghuSrikanthReddy。
  • @Bart 的评论是正确的,虽然有点不具体。他的意思是,我很确定,i 总是大于或等于 0,因为iunsigned int,这几乎但不完全是 Raghu 所说的。

标签: c for-loop


【解决方案1】:

在无符号类型中,最高有效位不被视为符号位。 在你的情况下 unsigned int 有4bytes(32 bits)

因此,当您递减“0”时,其完成方式是将1 中的2's complement 添加到0。 这是4294967295,是最大值,当所有 32 位都为 1 时,它什么都不是。 因此结果为4294967295

从最大值 4294967295 递增时(即当所有 32 位均为 1 时) 增加它会使所有低 32 位为 0,32nd 位为 1。因此,32nd 位存在溢出,超出了 unsigned int 的 4 字节范围。 因此该值变为 0。

一般来说,对于无符号类型,0....MaxValue 有一个环绕。 当在MaxValue 以上递增时,您从0 重新输入。 当递减到0 以下时,您从MaxValue 重新输入。

【讨论】:

  • 感谢您的回答! :D 问题已经解决了,不过... :)
【解决方案2】:

根据 C99 标准:

6.2.5 类型

9) [...] 涉及无符号操作数的计算永远不会溢出,因为 无法用生成的无符号整数表示的结果 type 以比最大数大一的数为模减少 可以由结果类型表示的值。

所以,这就是你的情况:

  • i == 1,因此i-- 会产生i == 0
  • i == 0,因此 i-- 导致环绕,i == UINT_MAX
  • i == UINT_MAX,因此 i-- 导致 i == UINT_MAX - 1 等等。

修复循环的一种方法是使用以下 (https://*.com/a/665773/676939):

for (i = size; i-- > 0;){
    /* yada yada yada */
}

执行相同操作的另一种方法如下 (https://*.com/a/665758/676939):

unsigned fake_i;
for (fake_i = size; fake_i > 0; i--){
    unsigned i = fake_i - 1;
    /* Do something with i */
}

【讨论】:

    【解决方案3】:

    让我们看看当i 接近0 时会发生什么。

    • i == 1:循环从i >= 0 开始正常执行。从i 中减去 1。现在i 包含 0。
    • i == 0:循环从i >= 0 开始正常执行。从i 中减去 1。因为i 是未签名的,所以它会环绕。因此,i 现在包含 4294967295。
    • i == 4294967295:循环正常执行,因为i >= 0
    • 等等...

    解决方案是测试其他内容(例如i > 0,如您的示例)或在每次迭代和循环时增加i,同时它小于文件的大小。

    【讨论】:

    • 不是先做增减操作再检查吗?因为在正常的for循环(即i++)中,当条件为i <= size时,最后一次迭代是针对i == size,而在退出时是i == size + 1
    • Angel:正确,但这与我的解释无关,因为条件检查后总是递减,递减后总是条件检查(如果我们方便地忘记了循环体)。
    • 是的,事实是我愚蠢地检查了错误的东西!你是对的,它会环绕,因为它不会变成负数并且会变得无限……我修复了它,将其更改为:for (i = size - 1; i < size; i--)。在我的情况下永远不会有问题,因为size 总是比 UINT_MAX 小得多! :D
    【解决方案4】:

    无符号整数始终为>= 0

    for (i = size - 1; i >= 0; i--) {etc.}
    

    如果iunsigned int,则为无限循环。

    【讨论】:

    • 这是否意味着当 i-- 被调用时 i 的值为 0 它会环绕自身?
    • @ckv 当i0 时,i - 1 的值等于UINT_MAX,这是一个正数。
    • 所以,这就是它不会停止的原因。 i 永远不会变成负数……有时我很愚蠢! :D 非常感谢!!!