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

gzjq41n4  于 2022-11-13  发布在  其他
关注(0)|答案(9)|浏览(176)

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

rjzwgtxy

rjzwgtxy1#

它似乎这个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 */
ny6fqffe

ny6fqffe2#

要排除所有内容,下面是一个临时禁用警告的示例:

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

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

dsf9zpds

dsf9zpds3#

TL;DR:如果有效,请避免使用或使用_Noreturn[[nodiscard]]__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未使用,并且设置是hardcore(-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]]。属性是C2 x的一个新特性。到目前为止,C2 x定义了四个属性,[[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运算符

X1 M27 N1 X可用作X1 M28 N1 X的替代物。

#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

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

laawzig2

laawzig24#

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

TL;DR

你可能想看一下Hedley,这是我写的一个公共域的单个C/C++头文件,它为你做了 * 很多 * 这些事情。我将在本文的最后放一个关于如何使用赫德利完成所有这些事情的简短部分。

禁用警告

#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(国际商会):使用MSVC语法,但请记住警告编号是完全不同的。例如:#pragma warning(disable:1478 1786) .
  • PGI/Nvidia:有一个diag_suppress编译指示:#pragma diag_suppress 1215,1444。请注意,all warning numbers increased by one in 20.7(第一个Nvidia HPC版本)。
  • TI(CCS):有一个diag_suppress pragma与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警告的支持,所以如果你关心GCC 7之前的版本,你应该这样做

#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以上版本:一个月23个月/一个月24个月
  • 金属撞击声:
  • Intel 13+(可能更早):x 1米27英寸/x 1米28英寸
  • 微软VC 15+(Visual Studio 9.0 / 2008):x 1米29英寸/x 1米30英寸
  • ARM 5.6+:x1个31英寸/x1个32英寸
  • TI 8.1+:x1个33毫米/x1个34毫米
  • Pelles C 2.90+(可能更早):x1立方米35立方米/x1立方米36立方米

如果内存允许的话,对于一些非常旧的GCC版本(如3.x、IIRC),push/pop pragma必须位于函数的 * 外部 *。

隐藏着血腥的细节#

对于大多数编译器来说,可以使用_Pragma来隐藏宏背后的逻辑,这是在C99中引入的。最大的例外是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的工作原理。您无需深入研究大量文档和/或安装尽可能多的编译器版本来进行测试,您只需包含赫德利(它是一个公共域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_DIAGNOSTIC_DISABLE_DEPRECATED不是赫德利知道的唯一警告,也不是Hedley所能做的全部禁用警告,但希望您能理解。

niwlg2el

niwlg2el5#

#pragma GCC diagnostic ignored "-Wformat"

将“-Wformat”替换为警告标志的名称。
AFAIK无法对该选项使用push/pop语义。

9w11ddsr

9w11ddsr6#

用途:

#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 directives and the __pragma and _Pragma keywords *。
您至少需要4.02版本才能为GCC使用这些pragma,我不确定MSVC和Clang的版本。
GCC的push pop pragma处理看起来有点问题。如果你再次启用警告,你仍然会收到DISABLE_WARNING/ENABLE_WARNING块内部的警告。对于GCC的一些版本,它可以工作,而对于一些版本则不行。

zi8p0yeb

zi8p0yeb7#

我在使用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
eqqqjvef

eqqqjvef8#

GCC风格通常使用标准的C构造或__attribute__扩展来告诉编译器更多关于您的意图的信息,而不是沉默警告。
例如,通过将赋值放在括号中(即if ((p=malloc(cnt)))而不是if (p=malloc(cnt))),可以取消有关将赋值用作条件的警告。
关于未使用的函数参数的警告可以通过一些奇怪的__attribute__(我永远记不住)或通过自赋值等方式来隐藏。
但一般来说,我更喜欢全局禁用任何警告选项,这些选项会为正确代码中发生的事情生成警告。

eeq64g8w

eeq64g8w9#

以下是在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以取得指涉。

相关问题