簡體   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