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


220

在Visual C ++中,可以使用#pragma warning (disable: ...)。我也发现在GCC中,您可以覆盖每个文件编译器的标志。我该如何针对“下一行”或使用GCC在代码区域周围使用push / pop语义呢?


1
可能在gcc禁用了特定警告的重复-糟糕,实际上,这个问题本身是一个欺骗(但不是封闭的)。恰好是出现在“相关”下的那个。无论如何,在SO上已经多次询问并回答了这个问题。
泰勒·麦克亨利

1
@ paxdiablo:我控制相反。我将警告级别提高了很多,并希望逐行挤压已经证实可以的警告。
马特·乔纳

4
@Tyler McHenry:如果您仔细检查过,您可能会注意到链接的问题包含每个文件的解决方案,恰好是我在我自己的问题中提到的解决方案不令人满意(我什至偷走了链接)。
马特·乔纳

6
@paxdiablo,编译器会给出错误肯定的结论,有时您想使用-Werror进行编译,但不要让这些错误肯定的语句阻止构建。因此禁用特殊情况并说明原因-在某些情况下是有意义的。在其他情况下也可能很方便-例如自动生成的代码会生成无害的警告,这些警告不太容易进入和更改(因为生成了代码),尽管在这种情况下,禁用每个文件的可能性更大解决方案。
ideaman42

Answers:


221

看来这是可以做到的。我无法确定所添加的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 */

14
一秒pushpop两秒-可能是一push开始就缺少另一个吗?
深渊。2012年

37
“ #pragma GCC诊断推送#pragma GCC诊断pop使GCC记住每次推送时的诊断状态,并在每次pop时恢复到该点。如果pop没有匹配的推送,则将还原命令行选项。 ” -从GCC手册:gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html
bobpaul 2013年

11
作为参考,gcc版本4.4.3支持错误/警告/忽略,但不支持push / pop
frankster 2013年

12
具有诊断推送/弹出功能的GCC的第一个版本是GCC 4.6.4。我是通过在GCC文档中
bitek 2014年

5
可惜这在实践中不起作用。在某些情况下,它会产生更多警告。或者,也许更正确地说,它在GCC 4.7到5.1中实际上不起作用。例如,请参见GCC不遵守“ pragma GCC诊断”来使警告静音
jww


31

TL; DR:如果可行,请避免使用,或使用,或__attribute__其他类似的说明符_Pragma

这是我的博客文章“ 在GCC和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)。

您可以执行5件事:

  • 尽可能完善源代码
  • 使用声明说明符,例如 __attribute__
  • _Pragma
  • #pragma
  • 使用命令行选项。

改善来源

最初的尝试应该是检查是否可以改进源代码以消除警告。在这种情况下,我们不希望仅仅因为这个原因而更改算法,因为(在最后一个元素之后)argc是多余的。!*argvNULL

使用声明说明符,例如 __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以消除专门针对认沽权的警告:

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

.PHONY: all
all: puts

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

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


2
回复:通过不给自变量一个名称improving the source来更改main的声明也可以int main(int, const char* argv[]) { ... },您告诉编译器它将不被使用。
杰西·奇斯霍尔姆

1
@JesseChisholm无法在函数定义中省略参数名称。见ISO / IEC9899的6.9.1功能definintions,§5“如果声明符包括参数类型列表中,每个参数的声明应包括标识符[...]”和正确所以代码将由拒绝gcc以及clang
Christian Hujer

1
另一种模式是将变量强制转换为void。实际上,我在一个项目中看到了以下宏:#define UNUSED(x) ((void)x)用于使警告静音。我认为是在ReactOS中吗?
Paul Stelian

1
我认为您此后不需要反斜杠,不是吗?我_Pragma("GCC diagnostic pop") \ 应该是_Pragma("GCC diagnostic pop")
加布里埃尔·斯台普斯

1
@GabrielStaples是的,感谢您的注意,我已经更新了答案。
Christian Hujer

20
#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)

看到https://gcc.gnu.org/onlinedocs/cpp/Pragmas.htmlhttp://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-via-pragmashttps://msdn.microsoft .com / de-DE / library / d9x1s805.aspx了解更多详细信息

您至少需要版本4.02才能将这些编译指示用于gcc,不确定有关版本的msvc和clang。

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


3
您是真正的MVP
zeboidlund

19
#pragma GCC diagnostic ignored "-Wformat"

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

AFAIK无法为此选项使用push / pop语义。


4
可惜这在实践中不起作用。在某些情况下,它会产生更多警告。或者,也许更正确地说,它在GCC 4.7到5.1中实际上不起作用。例如,请参见GCC不遵守“ pragma GCC诊断”来使警告静音
jww

6

我对ROS标头等外部库也遇到了同样的问题。我喜欢在CMakeLists.txt中使用以下选项进行更严格的编译:

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

但是,这样做也会在外部包含的库中引起各种pedantic错误。解决方案是在包含外部库之前禁用所有pedantic警告,并重新启用,如下所示:

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

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

//restore compiler switches
#pragma GCC diagnostic pop

2
gcc的系统 目录是否应该更好地解决这个问题?
红色XIII

@RedXIII-是的,如果您可以列出此类目录并在gcc命令行中指定,则可以选择。但是,很多时候编译器会在管道中深入调用,否则您将无法控制其他人应该如何编译您的代码。在这些情况下,以上可能是一个更好的解决方案。
Shital Shah,

5

我知道问题是关于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
  • 叮当声:#pragma clang diagnostic ignored "-W…"。语法基本上与GCC相同,并且许多警告名称相同(尽管很多不是)。
  • 英特尔C编译器:使用MSVC语法,但请记住警告号完全不同。范例:#pragma warning(disable:1478 1786)
  • PGI:有一个语用说明diag_suppress#pragma diag_suppress 1215,1444
  • TI:存在diag_suppress与PGI语法相同(但警告号不同!)的杂语:pragma diag_suppress 1291,1718
  • Oracle Developer Studio(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:也使用diag_suppress类似PGI和TI,但是语法不同。一些警告数字是相同的,但我其他人则有所不同:#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
  • lang:#pragma clang diagnostic push/#pragma diagnostic pop
  • Intel 13+(可能更早):#pragma warning(push)/#pragma warning(pop)
  • MSVC 15+(VS 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编译指示必须在功能之外

隐藏血腥细节

对于大多数编译器,可以使用_PragmaC99中引入的来将逻辑隐藏在宏后面。即使在非C99模式下,大多数编译器也支持_Pragma。最大的例外是MSVC,它具有自己的__pragma关键字和不同的语法。该标准_Pragma采用字符串,Microsoft的版本不采用:

#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以及其他可能的组件上调用已弃用函数的警告(在更新Hedley时,我可能不会费心更新此答案)。而且,在未知的编译器上,宏将被预处理为零,因此您的代码将继续与任何编译器一起使用。当然HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED不是唯一的警告赫德利知道,也不是警告,禁止所有赫德利可以做,但希望你的想法。


3

gcc样式通常不是使用警告,而是使用标准C构造或__attribute__扩展名来告知编译器您的意图。例如,通过将分配放在括号中(即if ((p=malloc(cnt)))而不是)来抑制有关用作条件的警告if (p=malloc(cnt))。关于未使用的函数参数的警告可以通过一些__attribute__我不记得的奇怪的东西或通过自我分配等方式来抑制。但是通常,我更喜欢全局禁用任何会为将在正确代码中发生的事情生成警告的警告选项。


2
可能是吧。我的目的不是证明任何一般情况下的模式,而是观察gcc的警告抑制哲学似乎是什么。
R .. GitHub停止帮助ICE,2010年

带有加括号的编译器的警告/警告/警告行为不同?!?!!!! ?? !!!! 哇!真是出乎意料。
杰森S

1
@JasonS parens不会更改编译器的警告警告行为,它所做的是更改语句的语义。多余的parens使编译器完成分配并保留其最终值作为表达式,这不值得警告。如果您想if ((p=malloc(cnt)) != NULL) ...弄清楚,您可以说是编译器在后台执行的操作。
杰西·奇斯霍尔姆

@JesseChisholm:我认为您的解释不正确。
R .. GitHub STOP HELPING ICE

3

对于那些发现此页面并在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

请参阅http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0472m/chr1359124244797.html作为参考。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.