我遇到了一些与Clang -Wconditional-uninitialized
有关的异常行为。考虑以下示例:
typedef struct { int x[1]; } test_t;
test_t foo(void);
test_t foo() {
test_t t;
for(int i = 0; i < 1; i++) t.x[i] = 0;
return t;
}
int main() { }
使用例如
进行编译clang -o test -Weverything test.c
给出以下警告:
test.c:6:12: warning: variable 't' may be uninitialized when used here [-Wconditional-uninitialized]
return t;
^
test.c:4:5: note: variable 't' is declared here
test_t t;
^
1 warning generated.
但是,切换线路:
for(int i = 0; i < 1; i++) t.x[i] = 0;
使用
t.x[0] = 0;
不发出警告。请注意,使用-Wconditional-uninitialized
就足够了,而不必传递-Weverything
。两种情况下的-O3
优化组合都是相同的,两种情况下都发出了一个xorl %eax, %eax
。这是我可以创建的显示此行为的最小示例。
我在Clang的-Weverything
输出中放了一些盐,但是在我看来,这就像一个错误,我很困惑。我正在调用某种未定义的行为来导致这种情况,还是误报?启用-Wconditional-uninitialized
通常是个好主意吗?我在Linux的Clang 3.8.1和macOS的(Apple)9.1.0上看到了相同的行为。
答案 0 :(得分:0)
如上面评论中的@R ..所示,我已将其报告为LLVM bug 38856。显然,根据他们的说法:
-Wconditional-uninitialized预期具有误报:如果简单的控制流分析无法证明在每次使用之前已写入变量,则会发出警告。它对字段不敏感,对数据流也不敏感,因此它无法区分写一个数组元素和写整个数组之间的区别,并且不知道循环实际上总是至少执行一次。
-相反,只有在我们可以证明您的程序存在错误(或包含无效代码)时,才会进行警告。
我们可以进行一些改进以降低误报率(某些原始字段灵敏度可能相对容易添加),但是我们不想在此处重新创建clang静态分析器的全部智能,并且保持此警告足够简单以使我们可以将其作为常规编译的一部分来运行。
我在该报告中还指出,即使其中一个数组成员明显未初始化,以下示例也未给出警告:
#include <stdio.h>
typedef struct { int x[2]; } test_t;
test_t foo_3(void);
test_t foo_3() {
test_t t;
t.x[0] = 0;
return t;
}
int main() {
test_t t;
t = foo_3();
printf("%i %i\n", t.x[0], t.x[1]);
}
谁给我答复:
正如我在注释1中提到的那样,我们当前执行的分析无法分辨写入一个数组元素和写入整个数组之间的区别。在简单的情况下,应该相对简单地进行改进,因此我认为这个错误是有效的(但是优先级较低,因为预期该警告会带来误报)。
因此-Wconditional-uninitialized
以提供误报而闻名。显然,静态分析器并不像我期望的那样彻底。令我惊讶的是,在分析器似乎更好地工作的更复杂场景下,我还没有遇到过类似的事情,但是正如@toohonestforthissite在上面的注释中也指出的那样,通过像这样的结构按值传递数组不是在C中要做的标准事情。
-Wno-conditional-uninitialized
或#pragma clang diagnostic ignored "-Wconditional-uninitialized"
应该对那些想在这种情况下使用-Weverything
的人保持沉默。