当i达到-1时,由于整数溢出,下面的代码将产生一个段错误.如果我将"unsign 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;
}

推荐答案

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

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

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

将测试和递减结合在一起会产生对有符号和无符号索引类型都有效的classic 向下循环:

#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;
}

测试i-- > 0对于i == 0只是FALSE,但是i作为副作用递减,所以第一次迭代使用循环体内的值9,第二次使用8...最后一次使用0,即最后一次递减后的值i.Next将计算为FALSE,并将值UINT_MAX保留为i.

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


还请注意,i-- > 0可以写为i --> 0,如this popular question中所述.i-- > 0在C中是惯用的,而i --> 0不是.其中一个是否比另一个更优雅是见仁见智的

C++相关问答推荐

ISO_C_BINDING,从Fortran调用C

Tiva TM4C123GXL的I2C通信

GCC引发不明确的诊断消息

为什么内核使用扩展到前后相同的宏定义?

测量ARM MCU中断延迟的问题

当我更改编译优化时,相同的C代码以不同的方式运行

如何在不使用其他数组或字符串的情况下交换字符串中的两个单词?

文件权限为0666,但即使以超级用户身份也无法打开

Clang警告称,`ffi_type`与`sizeof`不兼容

如何在C++中安全地进行浮点运算

类型定义 struct 与简单的类型定义 struct

为什么GCC-O1优化破解了这个代码,为了一个GameBoy高级只读存储器而修改了VRAM的循环?

为什么二进制文件的大小不会随着静态数据的大小而增加?

在吉陀罗中,_2_1_和CONCAT11是什么意思?

当用C打印过多的';\n';时输出不正确

将char*数组深度复制到 struct 中?

与指针的原始C数组或C++向量<;向量<;双>>;

C 和 C++ 标准如何告诉您如何处理它们未涵盖的情况?

在 C 中传递参数时出现整数溢出

strlen 可以是[[未排序]]吗?