![](/img/trans.png)
[英]Why does not gcc and clang warn for unused result of library functions?
[英]Why do gcc and clang not warn about writing to address 0?
以下错误代码:
#include <stdio.h>
#include <string.h>
void isEven (int *isFlag, int num) {
if (num % 2 == 0) {
*isFlag = 1;
} else {
*isFlag = 0;
}
}
int main() {
int num = 4;
int *isFlag = 0;
isEven(isFlag, num);
printf("%d", isFlag);
}
最近在这里发布了一个问题。 问题本身并不重要,重要的是(对我而言),虽然 gcc 和 clang 警告使用isFlag
作为printf()
的参数,但他们都没有警告地址 0 或 Z37A6259CC40C1DAE299A 指针是如何写入的至。 即使指定了-O3
,也要确保isEven()
function 是内联的,并且当我还指定-Wall -Wextra
时。
在这种情况下不应该有警告吗?
取消引用 null 指针是未定义的行为。 不需要为未定义的行为发出诊断(错误或警告)。 因此,从标准的角度来看,不为您的示例产生任何警告是完全可以的。
毫无疑问,让编译器检测到它会很有用,它可能无法在所有情况下都检测到。 gcc 确实有-Wnull-dereference
可以为您的示例检测并产生:
$ gcc -O3 -Wall -Wextra -Wnull-dereference -fsanitize=address test.c
test.c: In function ‘main’:
test.c:14:14: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
14 | printf("%d", isFlag);
| ~^ ~~~~~~
| | |
| int int *
| %ls
test.c:4:17: warning: null pointer dereference [-Wnull-dereference]
4 | *isFlag = 1;
| ~~~~~~~~^~~
来自gcc 文档:
-Wnull-取消引用
如果编译器检测到由于取消引用 null 指针而触发错误或未定义行为的路径,则发出警告。 此选项仅在 -fdelete-null-pointer-checks 处于活动状态时才处于活动状态,大多数目标中的优化都启用了该选项。 警告的精度取决于使用的优化选项。
这仅仅是因为编译器不够聪明,无法做到这一点。 有可能让它们变得如此聪明,但最有可能的是,编译器构造函数根本不认为值得付出努力。 因为归根结底,编写正确的代码是程序员的责任。
事实上,他们非常聪明。 正如 OznOg 在下面的评论中提到的,优化器正在使用这样的东西来使代码更快。 但标准并不要求编译器为此发出警告,C 有一种非常重视程序员的心态。 它根本不像 Java 那样握住你的手。
此外,这种警告很容易产生大量误报。 在您的代码中,这是相当微不足道的,但可以很容易地想象带有分支的更复杂示例。 像这样的代码:
int *q = malloc(*q);
int *p = 0;
if(n%2 == 0) p = q;
*p = 42;
在最后一行,如果n
是奇数,我们将写入NULL
,如果n
是偶数,我们将写入q
。 并且q
是一个有效的地址。 好吧,前提是malloc
成功了。 这也应该产生警告吗?
gcc
有一个编译器选项,可能还有其他可以给你的。 但是,标准没有要求。 而默认不启用的原因与标准为什么不要求基本相同。
在这种情况下,正确的处理方法是在 function 中添加 null 检查。 像这样:
void isEven (int *isFlag, int num) {
if(!isFlag) {
/* Handle error. Maybe error message and/or exit */
} else if (num % 2 == 0) {
*isFlag = 1;
} else {
*isFlag = 0;
}
}
C 编译器通常不记得值。 例如,这会生成警告warning: division by zero
:
int y = 1/0;
但不是这个:
int x = 0;
int y = 1/x;
检测这些错误的一种方法是使用--fsanitize=address
进行编译。 您不会在编译时发现它,而是在运行时发现它。
在这种情况下不应该有警告吗?
我正要说一些关于单独编译等的事情,但我发现 GCC 8.4 也没有诊断出main()
中该指针的取消引用,即使使用标志-Wall -Wextra -pedantic
。
应该吗? 没有要求它这样做。 该行为未定义,但不是违反约束,因此 C 不需要实现来诊断它。 如果编译器确实诊断出这一点,那肯定会有所帮助——也许有些人会这样做——但这归结为实施质量问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.