安全倒计时循环

Ama*_*ati 1 c reverse for-loop unsigned-integer

当 i 达到 -1 时,以下代码将由于整数溢出而产生段错误。如果我将“unsigned int i”更改为“char i”,那么它将正常工作,但会生成编译器警告“数组下标具有类型'char'”。将其声明为“int i”会很好地工作,并且不会出现编译器警告,但感觉应该有。毕竟 int 也是有符号的,并且也可能变为负值。我的问题是,是否有一种安全、优雅、惯用的方式在 C 中编写此类循环?

#include <stdio.h>

int main() {
    unsigned int i;
    char a[10] = {0};

    for (i = 9; i >= 0; i--) {
        printf("a[%d]: %d\n", i, a[i]);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

chq*_*lie 5

代码中的循环确实不起作用,因为测试i >= 0始终为真。带有额外警告的编译会发现这个问题。

为了避免这个问题,i应该初始化为10,测试应该i > 0并且i应该在每次迭代开始时递减,而不是在迭代之后递减:

    for (unsigned i = 10; i > 0;) {
        i--;
        printf("a[%d]: %d\n", i, a[i]);
    }
Run Code Online (Sandbox Code Playgroud)

将测试和减量结合起来会产生适用于有符号和无符号索引类型的经典向下循环:

#include <stdio.h>

int main(void) {
    char a[10] = { 0 };

    for (unsigned i = sizeof(a) / sizeof(*a); i-- > 0;) {
        printf("a[%u]: %d\n", i, a[i]);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

测试i-- > 0仅对 为 false i == 0,但i会作为副作用而递减,因此第一次迭代使用9循环体内的值,第二次使用8... ,最后一次使用,即上次递减后0的值。i下一个 next 将评估为 false 并保留iUINT_MAX

这种技术的另一个优点是i初始化为元素数量 ,10这也是迭代次数,而不是9像问题代码中那样。


另请注意,可以按照这个常见问题中的解释来i-- > 0编写。虽然在 C 语言中是惯用的,但不是。一个比另一个更优雅是见仁见智的问题i --> 0i-- > 0i --> 0