例如:
在这种情况下,我想有一个编译器警告。
这可能吗?
#include <stdio.h>
int main(void)
{
char i;
int count = 555;
for(i = 0; i < count; i++)
printf("%d\n", i);
return 0;
}
因为这里的核心问题是整数溢出/赋值超出范围。如果
char
已签名,则它是运行时实现定义或未定义的行为,而不是真正的编译器业务。最终(不幸的是)C 程序员有责任了解和发现代码中所有形式的定义不当的行为。
编译器的诊断能力也往往很差,他们的工作是检查您的代码是否有效 C,如果不是则通知您。如果你幸运的话,他们可能还会提醒你常见的错误和未定义的行为,但这不是你可以指望的,它只是一个奖励。
关于隐式提升,在 gcc 中有
-Wconversion
但这个选项非常不稳定且不可靠。它不会在您的情况下发出警告,而在其他情况下会发出错误警告。
最好的选择是使用称为“静态分析器”的外部工具。它们类似于编译器,但专注于查找有问题的代码和错误。有一些开源工具,如 clang-tidy 和 Frama-C,但大多数此类工具都是商业的。
此类静态分析器的一种形式是“MISRA 检查器”,用于验证是否符合 MISRA C 准则。 MISRA C 的一个重要部分致力于查找隐式提升/意外类型更改错误,因此在这种情况下,我建议获得 MISRA C 静态分析器。
有没有办法在整数提升时得到警告?
OP 的担忧是真实的,但在 integer promotion.
上错位了编译器选项的存在是为了帮助 OP,但它们的出现是有选择性的。
i++
就像 i = i + 1
(但 i
只计算了一次)并且对于 8 位 i
,从 char
到 int
的提升在添加之前被明确定义。添加定义明确,也在范围内。
将
int
分配给具有超出范围的值的 char
实际上是 integer demotion 和对 OP 的合理关注。一些编译器提供选项来捕获其中的 some。 “否则,新类型已签名,并且无法在其中表示值;结果是实现定义的,或者引发了实现定义的信号。”适用。
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)
注意:这里没有符号整数溢出,也没有未定义的行为。有实现定义的行为。
您可以使用
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
变量的有限范围引起的无限循环,这很奇怪,因为 他们确实检测到它并利用它:
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 assembly code:
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"
这是疯狂优化的一个简单而有说服力的例子:编译器检测到一个明显无意的无限循环,他们没有向程序员报告这个发现,而是直接生成失控代码。