如何为几行代码禁用 GCC 警告

Posted

技术标签:

【中文标题】如何为几行代码禁用 GCC 警告【英文标题】:How to disable GCC warnings for a few lines of code 【发布时间】:2011-03-23 15:44:24 【问题描述】:

在 Visual C++ 中,可以使用#pragma warning (disable: ...)。我还发现在 GCC 你可以override per file compiler flags。如何为“下一行”或使用 GCC 的代码区域周围的推送/弹出语义执行此操作?

【问题讨论】:

disable specific warnings in gcc 的可能重复项 - 糟糕,实际上这个问题本身就是一个骗局(但没有关闭)。那恰好是出现在“相关”下的那个。无论如何,这已经在 SO 上被问过并回答了好几次。 @paxdiablo:我反其道而行之。我已经将警告级别提高到了非常高的水平,并希望逐行消除我已验证没问题的警告。 @Tyler McHenry:如果您仔细检查,您可能会注意到链接的问题包含每个文件的解决方案,正是我在自己的问题中提到的不令人满意的解决方案(我什至偷了链接) . @paxdiablo,编译器会给出误报,有时您想使用 -Werror 进行编译,但不会让这些误报阻止构建。因此禁用特定情况并评论原因 - 在某些情况下是有意义的。在其他情况下这也很方便 - 例如自动生成代码会产生无害的警告,这些警告不容易进入和更改(因为生成了代码),但在这种情况下禁用每个文件更有可能是解决方案。 【参考方案1】:
#pragma GCC diagnostic ignored "-Wformat"

将“-Wformat”替换为警告标志的名称。

AFAIK 无法为此选项使用推送/弹出语义。

【讨论】:

很遗憾这在实践中不起作用。在某些情况下,它会产生更多警告。或者,更准确地说,它在 GCC 4.7 到 5.1 的实践中不起作用。例如,请参阅GCC does not honor 'pragma GCC diagnostic' to silence warnings。【参考方案2】:

GCC 风格通常不是消除警告,而是使用标准 C 构造或 __attribute__ 扩展来告诉编译器更多关于您的意图。

例如,通过将赋值放在括号中,即if ((p=malloc(cnt))) 而不是if (p=malloc(cnt)),可以抑制关于赋值用作条件的警告。

关于未使用的函数参数的警告可以通过一些奇怪的__attribute__ 我永远记不住,或者通过自赋值等方式来抑制。

但通常我更喜欢全局禁用任何警告选项,该选项会为正确代码中发生的事情生成警告。

【讨论】:

也许是这样。我的目的不是证明任何一般的案例模式,而是观察 gcc 关于警告抑制的哲学。 编译器的行为与添加括号的警告不同?!?!??!!!!哇!这是出乎意料的。 @JasonS 括号不会改变编译器警告的行为,它所做的是改变语句的语义。额外的括号使编译器完成分配并将其最终值保留为表达式,这不值得警告。如果你想清楚,你可以说if ((p=malloc(cnt)) != NULL) ...,因为那是编译器在幕后所做的。 @JesseChisholm:我不认为你的解释是准确的。【参考方案3】:

这似乎是can be done。我无法确定它添加的 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 */

【讨论】:

一个push和两个pops - 可能是另一个push开头的缺失? "#pragma GCC diagnostic push #pragma GCC diagnostic pop 使 GCC 记住每次推送时的诊断状态,并在每次弹出时恢复到该点。如果弹出没有匹配的推送,命令行选项已恢复。” -- 来自 GCC 手册:gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html 供参考,gcc 4.4.3版支持error/warning/ignored,但不支持push/pop 第一个具有诊断推送/弹出功能的 GCC 版本是 GCC 4.6.4。我通过查看GCC Documentation 的每个 GCC 版本的 Diagnostic-Pragmas.html#Diagnostic-Pragmas 部分来确定这一点 很遗憾这在实践中不起作用。在某些情况下,它会产生更多警告。或者,更准确地说,它在 GCC 4.7 到 5.1 的实践中不起作用。例如,请参阅GCC does not honor 'pragma GCC diagnostic' to silence warnings。【参考方案4】:

为了解决所有问题,这是一个暂时禁用警告的示例:

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

您可以查看GCC documentation on diagnostic pragmas了解更多详情。

【讨论】:

应该可以,但我的gcc-4.9 完全忽略了这一行。【参考方案5】:

用途:

#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、Clang 和 MSVC 有用。

可以用例如:

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

请参阅 7 PragmasControlling Diagnostics via PragmasPragma directives and the __pragma and _Pragma keywords 了解更多详情。

您至少需要 4.02 版才能为 GCC 使用这些编译指示,我不确定 MSVC 和 Clang 的版本。

看起来 GCC 的 push pop pragma 处理有点坏。如果再次启用警告,您仍然会收到 DISABLE_WARNING/ENABLE_WARNING 块内的块的警告。对于某些版本的 GCC,它可以工作,而对于某些版本,则不能。

【讨论】:

你是真正的MVP【参考方案6】:

我对 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 的systemdirectories 来处理这个问题吗? @RedXIII - 是的,如果您可以列出此类目录并在 gcc 命令行中指定,这是一个选项。然而,很多时候编译器在管道深处被调用,或者你无法控制其他人应该如何编译你的代码。在上述情况下,可能是更好的解决方案。【参考方案7】:

这是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

参考official documentation。

【讨论】:

链接(实际上)已损坏。它重定向到这个通用(搜索)页面(并且没有 HTML POST 信息):https://developer.arm.com/documentation#sort=relevancy. @PeterMortensen 我已经用更新的链接更新了我的答案。【参考方案8】:

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

这可能不是您在特定情况下想要的,但它可能会帮助处于类似情况的其他读者。

【讨论】:

re: improving the source 也可以通过不给参数命名来将 main 的声明更改为 int main(int, const char* argv[]) ... ,告诉编译器它将未被使用。 @JesseChisholm 在函数定义中省略参数名称是不可能的。请参阅 ISO/IEC9899 的 6.9.1 函数定义,第 5 节“如果声明符包含参数类型列表,则每个参数的声明应包含标识符 […]”并且正确,因此代码也将被 gcc 拒绝作为clang 另一种模式是将变量强制转换为 void。事实上,我在一个项目中看到过以下宏:#define UNUSED(x) ((void)x) 用于静音警告。我认为是在 ReactOS 中? 我认为您在此之后不需要反斜杠,不是吗? _Pragma("GCC diagnostic pop") \ 应该只是 _Pragma("GCC diagnostic pop") 我认为。 @GabrielStaples 没错,感谢您的关注,我已经更新了答案。【参考方案9】:

我知道这个问题是关于 GCC 的,但是对于寻找如何在其他和/或多个编译器中执行此操作的人来说……

TL;DR

您可能想查看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 基本相同,并且许多警告名称相同(尽管很多不同)。 Intel C++ Compiler (ICC):使用 MSVC 语法,但请记住警告编号完全不同。示例:#pragma warning(disable:1478 1786)。 PGI/Nvidia:有一个diag_suppress pragma:#pragma diag_suppress 1215,1444。请注意all warning numbers increased by one in 20.7(第一个 Nvidia HPC 版本)。 TI (CCS):有一个 diag_suppress 杂注,其语法与 PGI 相同(但警告编号不同!):pragma diag_suppress 1291,1718 Oracle Developer Studio (ODS) (suncc):有一个 error_messages 杂注。令人讨厌的是,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 叮当声:#pragma clang diagnostic push / #pragma 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 必须在函数之外

隐藏血淋淋的细节

对于大多数编译器,可以使用_Pragma 隐藏宏背后的逻辑,C99 中引入了该逻辑。即使在非 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 所能做的所有禁用警告,但希望您明白这一点。

【讨论】:

以上是关于如何为几行代码禁用 GCC 警告的主要内容,如果未能解决你的问题,请参考以下文章

禁用所有 gcc 警告

如何使用 #pragma 和 gcc 选择性地禁用 -Werror

如何为生成的 service worker 禁用 workbox-webpack-plugin 的警告

如何为 std::vector 复制或使用隐式缩小转换禁用 Visual Studio 警告 C4244

如何禁用关于#warning 指令是 GCC 扩展的 GCC 警告?

在源代码中部分禁用 gcc 中的迂腐警告