比较char和unsigned char时如何启用编译器警告?

sag*_*agi 23 c unsigned signed gcc compiler-warnings

以下面的C代码为例:

int main(int argc, char *argv[])
{
    signed char i;
    unsigned char count = 0xFF;

    for (i=0; i<count;i++)
    {
        printf("%x\n", i);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

此代码在无限循环中运行,即使我按如下方式编译它:

# gcc -Wall -Wpedantic -Wconversion -Wsign-compare -Wtype-limits -Wsign-conversion test.c -o test
Run Code Online (Sandbox Code Playgroud)

有人知道应该警告这些问题的编译器标志吗?

为了清楚起见,我不是在问'为什么会得到一个无限循环',而是要知道是否有办法使用编译器或静态分析来阻止它?

250*_*501 13

标志-Wconversion不会捕获错误,因为比较中的两个操作数:i<count,使用整数提升升级到int.

gcc中没有标志可以捕捉到这一点.

除此之外,代码的行为是未定义的,因为变量i溢出,当它具有值0x7F并且递增时:i++.

如果要迭代某个值,请确保您使用的类型可以表示该值.

  • 我知道变量i溢出,这正是我想要实现的.我不是问'为什么'它在无限循环中运行,以及如何解决它.我想知道编译器是否有一种方法可以捕获这个问题? (12认同)
  • 一旦发生未定义的行为,就没有"修复".你只需要*避免*它. (3认同)

chq*_*lie 7

i是a signed char,将其递增超出SCHAR_MAX具有实现定义的效果.计算i + 1被推广之后进行iint它不溢出(除非sizeof(int) == 1SCHAR_MAX == INT_MAX).然而,这个值超出了范围,i并且由于i具有签名类型,结果是实现定义的或者引发实现定义的信号.(C11 6.3.1.3p3有符号和无符号整数).

根据定义,编译器是实现,因此为每个特定系统和x86架构定义行为,其中存储值导致屏蔽低位,gcc应该意识到循环测试肯定是不变的,使其成为无限的环.

请注意,clang它既不会检测常量测试,clang 3.9.0也会检测为if ,如果替换countconst,则会发出警告,不像.i < counti < 0xffgcc

编译器都没有抱怨签名/未签名的比较问题,因为两个操作数实际上都是int在比较之前提升的.

你在这里找到了一个有意义的问题,特别重要,因为一些编码约定坚持对所有变量使用尽可能小的类型,导致像int8_tuint8_t循环索引变量这样的奇怪.这样的选择确实容易出错,我还没有找到一种方法让编译器警告程序员有关你发布的错误等愚蠢的错误.

  • @ 2501:我在C11标准中寻找相关语言来支持这一点,并且找不到任何结论.你能详细说明为什么你认为`i ++`应该与`i + = 1`和/或`i = i + 1`表现不同? (2认同)