簡體   English   中英

如何為幾行代碼禁用 GCC 警告

[英]How to disable GCC warnings for a few lines of code

在 Visual C++ 中,可以使用#pragma warning (disable: ...) 我還發現,在 GCC 中,您可以覆蓋每個文件的編譯器標志 如何為“下一行”執行此操作,或者使用 GCC 在代碼區域周圍使用推送/彈出語義?

看來這是可以做到的。 我無法確定它添加的 GCC 版本,但它是在 2010 年 6 月之前的某個時間。

這是一個例子:

#pragma GCC diagnostic error "-Wuninitialized"
    foo(a);         /* error is given for this one */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuninitialized"
    foo(b);         /* no diagnostic for this one */
#pragma GCC diagnostic pop
    foo(c);         /* error is given for this one */
#pragma GCC diagnostic pop
    foo(d);         /* depends on command line options */

為了解決所有問題,這是一個暫時禁用警告的示例:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
    write(foo, bar, baz);
#pragma GCC diagnostic pop

您可以查看有關診斷編譯指示的 GCC 文檔以獲取更多詳細信息。

TL;DR :如果有效,請避免或使用__attribute__之類的說明符,否則_Pragma

這是我的博客文章Suppressing Warnings in GCC and Clang的簡短版本。

考慮以下Makefile

CPPFLAGS:=-std=c11 -W -Wall -pedantic -Werror

.PHONY: all
all: puts

用於構建以下puts.c源代碼:

#include <stdio.h>

int main(int argc, const char *argv[])
{
    while (*++argv)
        puts(*argv);
    return 0;
}

它不會編譯,因為argc未使用,並且設置是硬核( -W -Wall -pedantic -Werror )。

你可以做五件事:

  • 如果可能,改進源代碼
  • 使用聲明說明符,例如__attribute__
  • 使用_Pragma
  • 使用#pragma
  • 使用命令行選項。

改進源

第一次嘗試應該是檢查是否可以改進源代碼以消除警告。 在這種情況下,我們不想僅僅因為這個而改變算法,因為argc!*argv是多余的(最后一個元素后為NULL )。

使用聲明說明符,例如__attribute__

#include <stdio.h>

int main(__attribute__((unused)) int argc, const char *argv[])
{
    while (*++argv) puts(*argv);
    return 0;
}

如果幸運的話,該標准會為您的情況提供一個說明符,例如_Noreturn

__attribute__是專有的 GCC 擴展(由 Clang 和一些其他編譯器(如armcc )支持),許多其他編譯器不會理解。 如果您想要可移植代碼,請將__attribute__((unused))放入宏中。

_Pragma運算符

_Pragma可以用作#pragma的替代品。

#include <stdio.h>

_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored \"-Wunused-parameter\"")

int main(int argc, const char *argv[])
{
    while (*++argv)
        puts(*argv);
    return 0;
}
_Pragma("GCC diagnostic pop")

_Pragma運算符的主要優點是您可以將其放在宏中,而#pragma指令則無法做到這一點。

缺點:它幾乎是一個戰術核武器,因為它是基於行而不是基於聲明的。

_Pragma運算符是在C99中引入的。

#pragma指令。

我們可以更改源代碼以抑制代碼區域的警告,通常是整個函數:

#include <stdio.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
int main(int argc, const char *argv[])
{
    while (*++argc) puts(*argv);
    return 0;
}
#pragma GCC diagnostic pop

缺點:它幾乎是一個戰術核武器,因為它是基於行而不是基於聲明的。

請注意, Clang中存在類似的語法。

抑制單個文件的命令行上的警告

我們可以將以下行添加到Makefile以抑制專門針對 puts 的警告:

CPPFLAGS:=-std=c11 -W -Wall -pedantic -Werror

.PHONY: all
all: puts

puts.o: CPPFLAGS+=-Wno-unused-parameter

在您的特定情況下,這可能不是您想要的,但它可能會幫助處於類似情況的其他讀者。

我知道這個問題是關於 GCC 的,但是對於尋找如何在其他和/或多個編譯器中執行此操作的人來說……

TL;博士

您可能想看看Hedley ,這是我編寫的一個公共域單個 C/C++ 標頭,它為您做了很多這樣的事情。 在這篇文章的最后,我將簡要介紹如何使用 Hedley 來完成所有這些工作。

禁用警告

#pragma warning (disable: …)在大多數編譯器中都有等效項:

  • MSVC#pragma warning(disable:4996)
  • GCC: #pragma GCC diagnostic ignored "-W…" ,其中省略號是警告的名稱; 例如#pragma GCC diagnostic ignored "-Wdeprecated-declarations
  • Clang#pragma clang diagnostic ignored "-W…" 語法與 GCC 基本相同,並且許多警告名稱是相同的(盡管很多不是)。
  • 英特爾 C++ 編譯器(ICC):使用 MSVC 語法,但請記住警告編號完全不同。 示例: #pragma warning(disable:1478 1786)
  • PGI /Nvidia:有一個diag_suppress pragma: #pragma diag_suppress 1215,1444 請注意, 所有警告數量在 20.7 (第一個 Nvidia HPC版本)中都增加了一個。
  • TI (CCS):有一個diag_suppress pragma,其語法與 PGI 相同(但警告編號不同!): pragma diag_suppress 1291,1718
  • Oracle Developer Studio (ODS) (suncc):有一個error_messages pragma。 令人討厭的是,C 和 C++ 編譯器的警告是不同的。 這兩個都禁用了基本相同的警告:
    • C: #pragma error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)
    • C++: #pragma error_messages(off,symdeprecated,symdeprecated2)
  • IAR :也像 PGI 和 TI 一樣使用diag_suppress ,但語法不同。 一些警告數字是相同的,但我其他人有分歧: #pragma diag_suppress=Pe1444,Pe1215
  • Pelles C :類似於 MSVC,但數字再次不同#pragma warn(disable:2241)

對於大多數編譯器來說,在嘗試禁用它之前檢查編譯器版本通常是一個好主意,否則你最終會觸發另一個警告。 例如,GCC 7 添加了對-Wimplicit-fallthrough警告的支持,因此如果您在 7 之前關心 GCC,您應該執行類似的操作

#if defined(__GNUC__) && (__GNUC__ >= 7)
#  pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#endif

對於 Clang 和基於 Clang 的編譯器,例如更新版本的XL C/C++和 armclang,您可以使用__has_warning()宏檢查編譯器是否知道特定警告。

#if __has_warning("-Wimplicit-fallthrough")
#  pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#endif

當然,您還必須檢查__has_warning()宏是否存在:

#if defined(__has_warning)
#  if __has_warning("-Wimplicit-fallthrough")
#    pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#  endif
#endif

你可能會想做類似的事情

#if !defined(__has_warning)
#  define __has_warning(warning)
#endif

因此,您可以更輕松地使用__has_warning Clang 甚至在他們的手冊中為__has_builtin()宏提出了類似的建議。 不要這樣做 其他代碼可能會檢查__has_warning並在編譯器版本不存在時返回檢查,如果您定義__has_warning您將破壞他們的代碼。 正確的方法是在你的命名空間中創建一個宏。 例如:

#if defined(__has_warning)
#  define MY_HAS_WARNING(warning) __has_warning(warning)
#else
#  define MY_HAS_WARNING(warning) (0)
#endif

然后你可以做類似的事情

#if MY_HAS_WARNING(warning)
#  pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#elif defined(__GNUC__) && (__GNUC__ >= 7)
#  pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#endif

推動和彈出

許多編譯器還支持將警告推送和彈出堆棧的方法。 例如,這將禁用 GCC 上一行代碼的警告,然后將其返回到之前的狀態:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated"
call_deprecated_function();
#pragma GCC diagnostic pop

當然,編譯器之間在語法上並沒有很多一致意見:

  • GCC 4.6+: #pragma GCC diagnostic push / #pragma GCC diagnostic pop
  • Clang: #pragma clang diagnostic push / #pragma clang diagnostic pop
  • Intel 13+(可能更早): #pragma warning(push) / #pragma warning(pop)
  • MSVC 15+( Visual Studio 9.0 / 2008 ): #pragma warning(push) / #pragma warning(pop)
  • ARM 5.6+: #pragma push / #pragma pop
  • TI 8.1+: #pragma diag_push / #pragma diag_pop
  • Pelles C 2.90+(可能更早): #pragma warning(push) / #pragma warning(pop)

如果沒記錯的話,對於一些非常舊的 GCC 版本(如 3.x、IIRC),push/pop pragma 必須在函數之外

隱藏血淋淋的細節

對於大多數編譯器,可以使用C99中引入的_Pragma隱藏宏背后的邏輯。 即使在非 C99 模式下,大多數編譯器也支持_Pragma 最大的例外是 MSVC,它有自己的__pragma關鍵字和不同的語法。 標准_Pragma需要一個字符串,微軟的版本沒有:

#if defined(_MSC_VER)
#  define PRAGMA_FOO __pragma(foo)
#else
#  define PRAGMA_FOO _Pragma("foo")
#endif
PRAGMA_FOO

大致相當於,一旦預處理,

#pragma foo

這讓我們創建宏,這樣我們就可以編寫如下代碼

MY_DIAGNOSTIC_PUSH
MY_DIAGNOSTIC_DISABLE_DEPRECATED
call_deprecated_function();
MY_DIAGNOSTIC_POP

並隱藏宏定義中所有丑陋的版本檢查。

最簡單的方法:赫德利

既然您了解了如何在保持代碼整潔的同時以便攜方式執行此類操作的機制,那么您就了解了我的一個項目Hedley所做的事情。 無需深入研究大量文檔和/或安裝盡可能多的編譯器版本來進行測試,您只需包含 Hedley(它是一個公共域 C/C++ 頭文件)並完成它。 例如:

#include "hedley.h"

HEDLEY_DIAGNOSTIC_PUSH
HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
call_deprecated();
HEDLEY_DIAGNOSTIC_POP

將禁用有關在 GCC、Clang、ICC、PGI、MSVC、TI、IAR、ODS、Pelles C 以及可能的其他設備上調用已棄用函數的警告(我可能不會在更新 Hedley 時更新此答案)。 而且,在未知的編譯器上,宏將被預處理為空,因此您的代碼將繼續與任何編譯器一起使用。 當然, HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED不是 Hedley 知道的唯一警告,也不是 Hedley 所能做的所有禁用警告,但希望您明白這一點。

#pragma GCC diagnostic ignored "-Wformat"

將“-Wformat”替換為警告標志的名稱。

AFAIK 無法為此選項使用推送/彈出語義。

利用:

#define DIAG_STR(s) #s
#define DIAG_JOINSTR(x,y) DIAG_STR(x ## y)
#ifdef _MSC_VER
#define DIAG_DO_PRAGMA(x) __pragma (#x)
#define DIAG_PRAGMA(compiler,x) DIAG_DO_PRAGMA(warning(x))
#else
#define DIAG_DO_PRAGMA(x) _Pragma (#x)
#define DIAG_PRAGMA(compiler,x) DIAG_DO_PRAGMA(compiler diagnostic x)
#endif
#if defined(__clang__)
# define DISABLE_WARNING(gcc_unused,clang_option,msvc_unused) DIAG_PRAGMA(clang,push) DIAG_PRAGMA(clang,ignored DIAG_JOINSTR(-W,clang_option))
# define ENABLE_WARNING(gcc_unused,clang_option,msvc_unused) DIAG_PRAGMA(clang,pop)
#elif defined(_MSC_VER)
# define DISABLE_WARNING(gcc_unused,clang_unused,msvc_errorcode) DIAG_PRAGMA(msvc,push) DIAG_DO_PRAGMA(warning(disable:##msvc_errorcode))
# define ENABLE_WARNING(gcc_unused,clang_unused,msvc_errorcode) DIAG_PRAGMA(msvc,pop)
#elif defined(__GNUC__)
#if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406
# define DISABLE_WARNING(gcc_option,clang_unused,msvc_unused) DIAG_PRAGMA(GCC,push) DIAG_PRAGMA(GCC,ignored DIAG_JOINSTR(-W,gcc_option))
# define ENABLE_WARNING(gcc_option,clang_unused,msvc_unused) DIAG_PRAGMA(GCC,pop)
#else
# define DISABLE_WARNING(gcc_option,clang_unused,msvc_unused) DIAG_PRAGMA(GCC,ignored DIAG_JOINSTR(-W,gcc_option))
# define ENABLE_WARNING(gcc_option,clang_option,msvc_unused) DIAG_PRAGMA(GCC,warning DIAG_JOINSTR(-W,gcc_option))
#endif
#endif

這應該對 GCC、 ClangMSVC 有用

它可以被調用,例如:

DISABLE_WARNING(unused-variable,unused-variable,42)
[.... some code with warnings in here ....]
ENABLE_WARNING(unused-variable,unused-variable,42)

有關更多詳細信息,請參閱7 Pragmas通過 Pragma 和 Pragma 指令控制診斷以及 __pragma 和 _Pragma 關鍵字

您至少需要 4.02 版才能為 GCC 使用這些編譯指示,而且我不確定 MSVC 和 Clang 的版本。

看起來 GCC 的 push pop pragma 處理有點壞了。 如果您再次啟用警告,您仍然會收到 DISABLE_WARNING/ENABLE_WARNING 塊內的塊的警告。 對於某些版本的 GCC,它可以工作,而對於某些版本則不能。

我對ROS頭文件等外部庫也有同樣的問題。 我喜歡在CMakeLists.txt中使用以下選項進行更嚴格的編譯:

set(CMAKE_CXX_FLAGS "-std=c++0x -Wall -Wextra -Wstrict-aliasing -pedantic -Werror -Wunreachable-code ${CMAKE_CXX_FLAGS}")

但是,這樣做也會導致外部包含的庫中出現各種迂腐錯誤。 解決方案是在包含外部庫之前禁用所有迂腐警告,然后像這樣重新啟用它們:

// Save compiler switches
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"

// Bad headers with a problem goes here
#include <ros/ros.h>
#include <sensor_msgs/LaserScan.h>

// Restore compiler switches
#pragma GCC diagnostic pop

GCC 風格不是消除警告,而是使用標准 C 構造或__attribute__擴展來告訴編譯器更多關於您的意圖。

例如,通過將賦值放在括號中來抑制關於賦值用作條件的警告,即if ((p=malloc(cnt)))而不是if (p=malloc(cnt))

關於未使用的函數參數的警告可以通過一些我永遠記不得的奇怪的__attribute__或通過自賦值等來抑制。

但通常我更喜歡全局禁用任何會為正確代碼中發生的事情生成警告的警告選項。

這是在IAR中執行此操作的一種方法。 嘗試這個:

#pragma diag_suppress=Pe177
void foo1(void)
{
   /* The following line of code would normally provoke diagnostic 
      message #177-D: variable "x" was declared but never referenced.
      Instead, we have suppressed this warning throughout the entire 
      scope of foo1(). 
   */
   int x;
}
#pragma diag_default=Pe177

請參閱官方文檔以供參考。

暫無
暫無

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

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