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

在Visual c++中,可以使用#pragma warning (disable: ...)。此外,我发现在GCC中,你可以覆盖每个文件编译器标志。我怎么能做到这一点为“下一行”,或与推/弹出语义周围的代码区域使用GCC?

193958 次浏览
#pragma GCC diagnostic ignored "-Wformat"

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

AFAIK,这个选项没有办法使用push/pop语义。

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

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

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

但一般来说,我更喜欢全局禁用任何警告选项,这些选项会为正确的代码中发生的事情生成警告。

它显示为可以做到。我无法确定它添加的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诊断指南文档获取更多细节。

使用:

#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, 铿锵声MSVC有效。

它可以用例如:

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

详见7 Pragmas通过Pragmas控制诊断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

下面是在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

参考官方文档

博士TL;:如果有效,则避免使用或使用诸如_Noreturn[[nodiscard]]__attribute__之类的说明符,否则为_Pragma

这是我博客文章的一个简短版本 GCC和Clang中的抑制警告 < / em >。

考虑下面的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)。

你可以做以下五件事:

  • 如果可能的话,改进源代码
  • 使用属性,比如[[maybe_unused]]
  • 使用声明说明符,如__attribute__
  • 使用_Pragma
  • 使用#pragma
  • 使用命令行选项。

改进源代码

第一次尝试应该是检查源代码是否可以改进以消除警告。在这种情况下,我们不想仅仅因为这一点而改变算法,因为argc!*argv (NULL在最后一个元素之后)是冗余的。

使用属性,如[[maybe_unused]]

#include <stdio.h>


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

如果你幸运的话,标准为你的情况提供了一个属性,比如[[maybe_unused]]。属性是C2x的新特性。到目前为止,C2x定义了四个属性:[[deprecated]][[fallthrough]][[maybe_unused]][[nodiscard]]

使用声明说明符,如__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

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

注意,在铿锵声中存在类似的语法。

对单个文件取消命令行上的警告

我们可以在Makefile中添加以下行,以抑制专门针对看跌期权的警告:

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


.PHONY: all
all: puts


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

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

我知道这个问题是关于GCC的,但是对于那些寻找如何在其他和/或多个编译器中做到这一点的人来说……

博士TL;

你可能想看看赫德利,这是我写的一个公共域的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++编译器 (ICC):使用MSVC语法,但请记住警告数字是完全不同的。例子:#pragma warning(disable:1478 1786)
  • PGI/Nvidia:有一个diag_suppress pragma: #pragma diag_suppress 1215,1444。注意所有警告数字增加了20.7分之一(第一个Nvidia HPC版本)。
  • “透明国际” (CCS):有一个与PGI: pragma diag_suppress 1291,1718具有相同语法(但警告数字不同!)的diag_suppress pragma
  • Oracle开发者工作室 (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
  • 佩莱斯C:类似于MSVC,尽管数字也是不同的

对于大多数编译器,在尝试禁用它之前检查编译器版本通常是一个好主意,否则您将最终触发另一个警告。例如,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+: __abc0 / __abc1
  • Clang: #pragma clang diagnostic push / #pragma clang diagnostic pop
  • Intel 13+(可能更早):#pragma warning(push) / #pragma warning(pop)
  • MSVC 15+ (__abc2): __abc0 / __abc1
  • Arm 5.6+: __abc0 / __abc1
  • Ti 8.1+: __abc0 / __abc1
  • Pelles C 2.90+(可能更早):#pragma warning(push) / #pragma warning(pop)

如果没记错的话,对于一些非常旧的GCC版本(比如3。x, IIRC)的push/pop pragmas必须是函数的

隐藏血腥的细节

对于大多数编译器,可以使用_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所能做的全部,但希望你能理解。