繁体   English   中英

为什么 gcc 和 clang 没有警告写入地址 0?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM