在 Visual C++ 中,可以使用#pragma warning (disable: ...)
. 我还发现,在 GCC 中,您可以覆盖每个文件的编译器标志。如何为“下一行”执行此操作,或者使用 GCC 在代码区域周围使用推送/弹出语义?
9 回答
看来这是可以做到的。我无法确定它添加的 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 文档以获取更多详细信息。
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
缺点:它几乎是一个战术核武器,因为它是基于行而不是基于声明的。
运算符是在C99_Pragma
中引入的。
#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
在您的特定情况下,这可能不是您想要的,但它可能会帮助处于类似情况的其他读者。
我知道这个问题是关于 GCC 的,但是对于寻找如何在其他和/或多个编译器中执行此操作的人来说……</p>
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++ 编译器(ICC):使用 MSVC 语法,但请记住警告编号完全不同。示例:
#pragma warning(disable:1478 1786)
。 - PGI /Nvidia:有一个
diag_suppress
编译指示:#pragma diag_suppress 1215,1444
. 请注意,所有警告数量在 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)
- C:
- 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++__has_warning()
和 armclang,您可以使用宏检查编译器是否知道特定警告。
#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
- 英特尔 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 必须在函数之外。
隐藏血淋淋的细节
对于大多数编译器,可以使用C99_Pragma
中引入的隐藏宏背后的逻辑。即使在非 C99 模式下,大多数编译器也支持; 最大的例外是 MSVC,它有自己的关键字和不同的语法。该标准需要一个字符串,微软的版本没有:_Pragma
__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 所能做的所有禁用警告,但希望你明白这一点。
#pragma GCC diagnostic ignored "-Wformat"
将“-Wformat”替换为警告标志的名称。
AFAIK 无法为此选项使用推送/弹出语义。
利用:
#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
它可以被调用,例如:
DISABLE_WARNING(unused-variable,unused-variable,42)
[.... some code with warnings in here ....]
ENABLE_WARNING(unused-variable,unused-variable,42)
有关详细信息,请参阅7 Pragmas、通过 Pragma和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
GCC 风格通常不是消除警告,而是使用标准 C 构造或__attribute__
扩展来告诉编译器更多关于您的意图。
例如,通过将赋值放在括号中(即,if ((p=malloc(cnt)))
而不是if (p=malloc(cnt))
.
关于未使用的函数参数的警告可以通过一些__attribute__
我永远记不住的奇怪东西,或者通过自赋值等来抑制。
但通常我更喜欢全局禁用任何会为正确代码中发生的事情生成警告的警告选项。
这是在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
请参阅官方文档以供参考。