簡體   English   中英

為什么 gcc 和 clang 不對庫函數的未使用結果發出警告?

[英]Why does not gcc and clang warn for unused result of library functions?

考慮這個代碼:

int __attribute__((warn_unused_result)) foo(void)
{
    return 42;
}

int main(void)
{
    foo();
}

編譯時,它會發出警告:

$ gcc main.c
main.c: In function ‘main’:
main.c:8:5: warning: ignoring return value of ‘foo’, declared with attribute warn_unused_result [-Wunused-result]
    8 |     foo();
      |     ^~~~~

這正如預期的那樣。 我想知道為什么許多標准庫函數沒有用這個屬性聲明是否有任何理由。 我說的是像scanf這樣的函數,在大多數情況下,檢查返回值是至關重要的,還有像malloc這樣的函數,如果你不使用返回值,它是完全沒有意義的。 但是, realloc似乎有這個。

是否有任何理由不使用__attribute__((warn_unused_result))聲明 scanf、malloc 等函數? 我認為這可以防止許多錯誤。

思考為什么這些特定函數會得到warn_unused_result可能會warn_unused_result ——這可能有助於我們弄清楚為什么其他函數沒有得到該屬性。 在我的 Glibc 系統上,只有兩個這樣的函數:

extern void *realloc (void *__ptr, size_t __size)
     __THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2));
extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size)
     __THROW __attribute_warn_unused_result__
     __attribute_alloc_size__ ((2, 3));

在我的 macOS 系統上,一共有三個:

void    *malloc(size_t __size) __result_use_check __alloc_size(1);
void    *calloc(size_t __count, size_t __size) __result_use_check __alloc_size(1,2);
void    *realloc(void *__ptr, size_t __size) __result_use_check __alloc_size(2);

那么,為什么這個超級有用的屬性只用於realloc()和 Glibc 中另一個幾乎相同的函數?

答案是因為此屬性旨在防止非常具體的錯誤。 假設我們有一個 100 個元素的數組,並且想要調整它的大小以添加第 101 個元素(在索引 100 處)(讓我們忽略錯誤處理):

// Create 100-element array.
int *arr = malloc(sizeof(int) * 100);
arr[99] = 1234;
// Resize and add a 101st element.
realloc(arr, sizeof(int) * 101); // bug
arr[100] = 1234;

發現錯誤? 這是一個嚴重的內存錯誤,但它也是一個內存錯誤,可能會也可能不會很快被注意到。 最初的malloc通常會向上舍入到更大的大小,如果realloc成功,您最終可能會得到相同的地址。 只有當您開始寫入另一個對象,或者在arr的舊地址分配另一個對象時,才會注意到實際的錯誤。

這可能需要您花一些時間來調試。

因此,警告旨在捕獲這個難以調試的嚴重錯誤。 與以下內容進行比較:

malloc(100 * sizeof(int));

這個錯誤根本不是很嚴重——它只是一個內存泄漏。 它甚至不是錯誤的代碼!

作為一個有趣的練習,嘗試將上述代碼放入 Godbolt 並查看匯編輸出——GCC 實際上會完全刪除malloc的調用。

至於printfscanf等——

int r = printf(...);
if (r == -1) {
    abort();
}

如果 GCC 警告不檢查printf的返回,人們會感到沮喪,因為他們會在他們的代碼中看到太多警告——他們會通過關閉警告來做出回應。

所以有時最好只針對最嚴重的情況觸發警告,而讓其他一些情況下滑。


作為一個次要的技術說明,這對於標准庫維護者來說更像是一個問題,比如 Glibc 團隊,而不是 GCC 或 Clang。 盡管標准庫和編譯器緊密集成,但它們是獨立的項目——您甚至可以交換不同的標准庫。 例如,您可以使用 musl、glibc 或 Apple 的 libSystem。

  1. 它不便攜。
  2. 標准不需要它。
  3. 如果您想被警告,您可以隨時編寫包裝器。
static inline void * __attribute__((warn_unused_result, always_inline)) mymalloc(size_t size)
{
    return malloc(size);
}

int main(void)
{
    mymalloc(300);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM