gcc 有没有办法在整数升级时得到警告?

cnh2zyt3  于 2023-02-23  发布在  其他
关注(0)|答案(3)|浏览(236)

例如:
在这种情况下,我希望有一个编译器警告。
这可能吗?

#include <stdio.h>

int main(void)
{
    char i;
    int count = 555;
    
    for(i = 0; i < count; i++)
        printf("%d\n", i);
    
    return 0;
}
dohp0rv5

dohp0rv51#

因为这里的核心问题是整数溢出/赋值超出范围。如果char有符号,这是运行时实现定义或未定义的行为,而不是编译器真正的业务。(不幸的是)最终C程序员有责任知道并找到代码中所有形式的定义不当的行为。
编译器的诊断能力也很差,他们的工作是检查你的代码是否是有效的C代码,如果不是,就通知你。如果你幸运的话,他们可能还会给予你一些常见的bug和未定义的行为,但这不是你可以指望的,这只是一个额外的好处。
关于隐式促销,gcc中有-Wconversion,但是这个选项非常不稳定和不可靠,在您的情况下它不给予警告,在其他情况下它给出错误警告。
最好的选择是使用一个叫做“静态分析器”的外部工具。它们类似于编译器,但专注于发现有问题的代码和bug。有一些开源的工具,如clang-tidy和Frama-C,但大多数这样的工具都是商业化的。
这种静态分析器的一种风格是“MISRA检查器”,用于验证是否符合MISRA C指南。MISRA C的一个重要部分致力于查找隐式提升/意外类型更改bug,因此在这种情况下,我建议使用MISRA C静态分析器。

8aqjt8rx

8aqjt8rx2#

有没有办法在整数升级时得到警告?
OP的关注是真实的,但在 * 整数提升 * 上放错了地方。
编译器选项的存在是为了帮助OP,但它们在出现时可以是选择性的。
i++类似于i = i + 1(但是i只计算一次),并且对于8位i,从charint的提升在加法之前被很好地定义。
将一个int赋值给一个值超出范围的char实际上是一个 * 整数降级 *,也是OP的一个合理问题。一些编译器提供了捕捉其中 * 一些 * 的选项。"否则,新类型是有符号的,并且值不能在其中表示;结果是由实现定义的,或者产生由实现定义的信号。"适用。

char i;

// warning: conversion from 'int' to 'char' may change value [-Wconversion]
i = rand();

// But not here
i++;
i = i + 1;
i = (int) (i + 1);

使用printf("%d\n", i);时,char i会作为...参数的一部分以静默方式升级为int
这里不考虑价值。
i < count始终为真。整数提升不是问题,但i < count永远不会为假。
编译器提供了一些选项,如下面的警告 * 有时 *。代码分析工具提供了额外的警告。

// warning: comparison is always true due to limited range of data type [-Wtype-limits]
for(i = 0; i <= (char) 127; i = i + 1)
 
// No warning.
for(i = 0; i <= count; i = i + 1)

// No warning.
char b = 127;
for(i = 0; i <= b; i = i + 1)

// No warning and no integer promotion 
int m = INT_MAX;
for(int i = 0; i <= m; i = i + 1)

注意:这里没有有符号整数溢出,也没有 * 未定义的行为 *。有 * 实现定义的行为 *。

zi8p0yeb

zi8p0yeb3#

使用clang -Wall -pedantic可以得到的唯一警告与代码中的真实的问题无关:

<source>:3:9: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
int main()
        ^
         void

出于某种原因,gcc和clang都没有报告i变量的有限范围导致的无限循环,这很奇怪,因为they do detect it and take advantage of it
gcc 12.2 -O3装配代码:

.LC0:
        .string "%d\n"
main:
        push    rbx
        xor     ebx, ebx
.L2:
        movsx   esi, bl
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        add     ebx, 1
        call    printf
        jmp     .L2

clang 15.0 -O3装配代码:

main:
        push    rbp
        push    rbx
        push    rax
        lea     rbx, [rip + .L.str]
        xor     ebp, ebp
.LBB0_1:                                # =>This Inner Loop Header: Depth=1
        movsx   ebp, bpl
        mov     rdi, rbx
        mov     esi, ebp
        xor     eax, eax
        call    printf@PLT
        inc     bpl
        jmp     .LBB0_1
.L.str:
        .asciz  "%d\n"

这是疯狂优化的一个简单而生动的例子:这两个编译器都检测到了一个明显是无意的无限循环,并且没有向程序员报告这个发现,而是直接生成了失控的代码。2他们真可耻!

相关问题