![](/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.