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

Mat*_*ner 200 c gcc pragma compiler-warnings

在Visual C++中,可以使用#pragma warning (disable: ...).我还发现在GCC中你可以覆盖每个文件的编译器标志.我怎样才能为"下一行"做这个,或者使用GCC围绕代码区域推送/弹出语义?

Mat*_*ner 209

看来这可以做到.我无法确定它添加的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 */
Run Code Online (Sandbox Code Playgroud)

  • "#pragma GCC诊断推送#pragma GCC诊断弹出使GCC记住每次推送时的诊断状态,并在每次弹出时恢复到该点.如果弹出没有匹配的推送,则恢复命令行选项. " - 来自GCC手册:http://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html (33认同)
  • 一个`push`和两个`pop` - 可能是另一个`push`在开始时丢失了? (14认同)
  • 具有诊断推送/弹出功能的第一个GCC版本是[GCC 4.6.4](https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas).我通过查看[GCC Documentation](https://gcc.gnu.org/onlinedocs/)上每个GCC版本的Diagnostic-Pragmas.html#Diagnostic-Pragmas部分来确定这一点. (12认同)
  • 作为参考,gcc版本4.4.3支持错误/警告/忽略,但不支持push/pop (11认同)
  • 遗憾的是,这在实践中不起作用.在某些情况下,它会产生更多警告.或者,更准确地说,它在GCC 4.7到5.1的实践中不起作用.例如,参见[GCC不尊重'pragma GCC诊断'来消除警告](http://stackoverflow.com/q/31509434). (5认同)

小智 99

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

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
    write(foo, bar, baz);
#pragma GCC diagnostic pop
Run Code Online (Sandbox Code Playgroud)

您可以查看有关诊断编译指示GCC文档以获取更多详细信息.

  • 应该可以工作,但我的 `gcc-4.9` 只是完全忽略了这一行。 (2认同)

Chr*_*jer 26

TL; DR:如果一切正常,避免或使用像符__attribute__,否则_Pragma.

这是我的博客文章在GCC和Clang中抑制警告的简短版本 .

考虑以下 Makefile

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

.PHONY: all
all: puts
Run Code Online (Sandbox Code Playgroud)

用于构建以下puts.c源代码

#include <stdio.h>

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

它不会编译因为argc未使用,并且设置是hardcore(-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;
}
Run Code Online (Sandbox Code Playgroud)

如果你很幸运,标准会为你的情况提供一个说明符,比如_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") \
Run Code Online (Sandbox Code Playgroud)

_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
Run Code Online (Sandbox Code Playgroud)

缺点:它几乎是一个战术核武器,因为它是基于行的而不是声明的baesd.

在命令行上为单个文件禁止警告

我们可以添加以下行来Makefile专门为puts抑制警告:

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

.PHONY: all
all: puts

puts.o: CPPFLAGS+=-Wno-unused-parameter
Run Code Online (Sandbox Code Playgroud)

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

  • 另一种模式是将变量强制转换为 void。事实上,我在一个项目中看到了以下宏:`#define UNUSED(x) ((void)x)` 用于静音警告。我认为它是在 ReactOS 中? (3认同)
  • re:`改良源``也可以通过不给参数命名来将main的声明更改为`int main(int,const char * argv []){...}`,您可以告诉编译器将不被使用。 (2认同)
  • @JesseChisholm 在函数定义中省略参数名称是不可能的。请参见 ISO/IEC9899 的 6.9.1 函数定义,第 5 节“如果声明符包含参数类型列表,则每个参数的声明应包含标识符 […]”并且正确,因此代码也会被 `gcc` 拒绝如“叮当”。 (2认同)
  • 我认为在此之后您不需要反斜杠,不是吗?我认为 `_Pragma("GCC Diagnostic pop") \ ` 应该是 `_Pragma("GCC Diagnostic pop")` 。 (2认同)
  • 它们以`__attribute__`语法的方式工作,它巧妙地使用双括号`((``))`,所以,如果你的编译器不理解它,你可以`#define __attribute__(x)`,然后它们就会消失。 (2认同)

Mar*_*rdy 19

#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
Run Code Online (Sandbox Code Playgroud)

这应该是gcc,clang和msvc的诀窍

可以用例如:

DISABLE_WARNING(unused-variable,unused-variable,42)
[.... some code with warnings in here ....]
ENABLE_WARNING(unused-variable,unused-variable,42)
Run Code Online (Sandbox Code Playgroud)

看到https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html,http://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,它可以工作,但有些版本没有.

  • 你是真正的MVP (3认同)

Joe*_*e D 18

#pragma GCC diagnostic ignored "-Wformat"
Run Code Online (Sandbox Code Playgroud)

将"-Wformat"替换为警告标志的名称.

AFAIK没有办法为此选项使用推/弹语义.

  • 遗憾的是,这在实践中不起作用.在某些情况下,它会产生更多警告.或者,更准确地说,它在GCC 4.7到5.1的实践中不起作用.例如,参见[GCC不尊重'pragma GCC诊断'来消除警告](http://stackoverflow.com/q/31509434). (4认同)

nem*_*equ 18

我知道这个问题是关于 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
  • 叮当:#pragma clang diagnostic ignored "-W…"。语法与 GCC 基本相同,许多警告名称相同(尽管许多不同)。
  • 英特尔 C 编译器:使用 MSVC 语法,但请记住,警告编号完全不同。例子:#pragma warning(disable:1478 1786)
  • PGI/NVidia:有一个diag_suppress编译指示:#pragma diag_suppress 1215,1444. 请注意,所有警告编号在 20.7(第一个 NV HPC 版本)中都增加了一个
  • TI:有一个diag_suppresspragma 语法与 PGI 相同(但警告编号不同!):pragma diag_suppress 1291,1718
  • Oracle Developer Studio (suncc):有一个error_messagespragma。令人讨厌的是,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
Run Code Online (Sandbox Code Playgroud)

对于 clang 和基于 clang 的编译器(例如 XL C/C++ 和 armclang 的较新版本),您可以使用__has_warning()宏检查编译器是否知道特定警告。

#if __has_warning("-Wimplicit-fallthrough")
#  pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#endif
Run Code Online (Sandbox Code Playgroud)

当然,您还必须检查__has_warning()宏是否存在:

#if defined(__has_warning)
#  if __has_warning("-Wimplicit-fallthrough")
#    pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#  endif
#endif
Run Code Online (Sandbox Code Playgroud)

你可能会想做类似的事情

#if !defined(__has_warning)
#  define __has_warning(warning)
#endif
Run Code Online (Sandbox Code Playgroud)

所以你可以__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
Run Code Online (Sandbox Code Playgroud)

然后你可以做类似的事情

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

推动和弹出

许多编译器还支持将警告推入和弹出堆栈的方法。例如,这将在 GCC 上为一行代码禁用警告,然后将其返回到以前的状态:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated"
call_deprecated_function();
#pragma GCC diagnostic pop
Run Code Online (Sandbox Code Playgroud)

当然,编译器之间关于语法的共识并不多:

  • 海湾合作委员会 4.6+:#pragma GCC diagnostic push/#pragma GCC diagnostic pop
  • 叮当:#pragma clang diagnostic push/#pragma diagnostic pop
  • 英特尔 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需要一个字符串,微软的版本没有:

#if defined(_MSC_VER)
#  define PRAGMA_FOO __pragma(foo)
#else
#  define PRAGMA_FOO _Pragma("foo")
#endif
PRAGMA_FOO
Run Code Online (Sandbox Code Playgroud)

大致相当于,一旦预处理,

#pragma foo
Run Code Online (Sandbox Code Playgroud)

这让我们可以创建宏,以便我们可以编写如下代码

MY_DIAGNOSTIC_PUSH
MY_DIAGNOSTIC_DISABLE_DEPRECATED
call_deprecated_function();
MY_DIAGNOSTIC_POP
Run Code Online (Sandbox Code Playgroud)

并在宏定义中隐藏所有丑陋的版本检查。

简单的方法:赫德利

既然您了解了如何在保持代码整洁的同时可移植地执行此类操作的机制,您就会了解我的一个项目Hedley所做的事情。无需深入研究大量文档和/或安装尽可能多的编译器版本以进行测试,您只需包含 Hedley(它是一个公共域 C/C++ 头文件)并完成它。例如:

#include "hedley.h"

HEDLEY_DIAGNOSTIC_PUSH
HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
call_deprecated();
HEDLEY_DIAGNOSTIC_POP
Run Code Online (Sandbox Code Playgroud)

将禁用关于在 GCC、clang、ICC、PGI、MSVC、TI、IAR、ODS、Pelles 和可能的其他人上调用已弃用函数的警告(我可能不会在更新 Hedley 时费心更新这个答案)。而且,在未知的编译器上,宏将被预处理为空,因此您的代码将继续与任何编译器一起使用。当然,HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED这不是 Hedley 知道的唯一警告,也不是 Hedley 可以做的所有禁用警告,但希望您能明白。


Shi*_*hah 6

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

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

但是,这样做会导致外部包含的库中的所有类型的迂腐错误.解决方案是在包含外部库并重新启用之前禁用所有迂腐警告:

//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
Run Code Online (Sandbox Code Playgroud)

  • 不应该用gcc的[系统](https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html)[目录](https://gcc.gnu.org/onlinedocs/)来处理这个问题. GCC /目录的Options.html)? (2认同)

Ker*_*ron 5

这是在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
Run Code Online (Sandbox Code Playgroud)

请参阅官方文档以供参考。