C 有哪些有用的 GCC 标志?

除了设置 -Wall-std=XXX之外,C 语言中还有什么其他真正有用但不太为人所知的编译器标志?

我特别感兴趣的是任何额外的警告,以及/或在某些情况下将警告转换为错误,以绝对减少任何意外的类型不匹配。

76263 次浏览
man gcc

该手册充满了有趣的旗帜与良好的描述。但是,-Wall 可能会使 GCC 尽可能冗长。如果您想要更多有趣的数据,您应该查看 瓦尔荷恩或其他用于检查错误的工具。

错了将所有警告视为错误并停止编译。gcc手册页解释了编译器的每个命令行开关。

我对任何额外的警告都特别感兴趣,

除了 之外,W- 是的选项(-W适用于较老版本的 gcc 以及较新版本; 较新版本支持另一个名称 -Wextra,意思相同,但更具描述性)支持各种额外的警告。

还有更多的警告是这两者都没有启用的,通常是针对那些可能更糟糕的事情。可用的选项集取决于您正在使用的 gcc 版本-请参阅 man gccinfo gcc了解详细信息,或者参阅 网上文件了解您感兴趣的特定 gcc 版本。而且 迂腐会发出特定标准所需的所有警告(这取决于其他选项,如 - std = xxxAnsi) ,并抱怨 gcc 扩展的使用。

和/或在某些情况下将警告转化为错误,以绝对减少任何意外类型 不匹配。

-Werror将所有警告转换为错误。但是我不认为 gcc 允许您针对特定的警告有选择地这样做。

您可能会发现,必须在每个项目的基础上选择启用哪些警告(特别是如果使用 -Werror) ,因为来自外部库的头文件可能会绊倒其中一些警告。(根据我的经验,-pedantic在这方面尤其没有帮助。)

- 是的也应该是标准的。错了将警告转换为错误(这可能会让 非常感到恼火,尤其是在不使用 没有结果进行编译时)。如果您使用 C99特性,迂腐Std = c89的组合会给您额外的警告。

但仅此而已,你不能将 C 编译器调优到比 C 本身更安全的类型。

就我而言,最有用的标志是 G,它将调试信息放入可执行文件,这样你就可以调试它,并在程序执行时逐步通过源代码(除非你熟练地阅读汇编并喜欢 stepi命令)。

始终使用 -O或以上(-O1-O2-Os等)。在默认的优化级别,gcc 追求编译速度,并且没有做足够的分析来警告单一化变量之类的事情。

考虑制定 -Werror策略,因为不停止编译的警告往往会被忽略。

-Wall基本上打开了很可能是错误的警告。

包含在 -Wextra中的警告倾向于标记公共的、合法的代码。它们对于代码审查可能是有用的(尽管 lint 风格的程序发现更多的陷阱更加灵活) ,但是我不会在正常的开发中使用它们。

如果项目中的开发人员不熟悉浮点数,那么 -Wfloat-equal是一个好主意,如果他们不熟悉浮点数,那么 -Wfloat-equal就是一个坏主意。

-Winit-self是有用的; 我想知道为什么它不包括在 -Wuninitialized中。

如果您的代码基本上都是可移植的,而且不能与 -pedantic一起工作,那么 -Wpointer-arith是非常有用的。

我有时使用 是的处理一个小得多的可执行文件:

-s
Remove all symbol table and relocation information from the executable.

资料来源: Http://gcc.gnu.org/onlinedocs/gcc/Link-Options.html # Link-Options”rel = “ nofollow noReferrer”> Options for Link

-f代码生成的一些选项很有意思:

以前这个答案也提到了 -ftrapv,但是这个功能已经被 -fsanitize=signed-integer-overflow取代,-fsanitize=signed-integer-overflow-fsanitize=undefined启用的消毒剂之一。

这是我的:

  • -Wextra -Wall: 必不可少。
  • -Wfloat-equal : 非常有用,因为通常测试浮点数的相等性是不好的。
  • -Wundef : 如果在 #if指令中计算了未初始化的标识符,则发出警告。
  • -Wshadow : 当一个局部变量隐藏另一个局部变量、参数或全局变量时,或者当一个内置函数隐藏时,发出警告。
  • -Wpointer-arith : 如果函数的大小或者 void的大小决定了什么,就发出警告。
  • -Wcast-align : 每当指针被强制转换时发出警告,以增加所需的目标对齐方式。例如,如果机器上的 char *被强制转换为 int *,而整数只能在两个或四个字节的边界上访问,则发出警告。
  • -Wstrict-prototypes : 在没有指定参数类型的情况下声明或定义函数时发出警告。
  • -Wstrict-overflow=5 : 警告编译器基于假设不发生有符号溢出而进行优化的情况。(值5可能过于严格,请参阅手册页。)
  • -Wwrite-strings : 给字符串常量类型 const char[长度],以便将其中一个的地址复制到一个非 const char *指针中将得到警告。
  • -Waggregate-return : 如果定义或调用了任何返回结构或联合的函数,则发出警告。
  • -Wcast-qual : 每当转换指针以从目标类型 *中删除类型限定符时发出警告。
  • -Wswitch-default : 每当 switch语句没有 default case*时发出警告。
  • -Wswitch-enum : 每当 switch语句具有枚举类型的索引并且对于该枚举 *的一个或多个命名代码缺少 case时发出警告。
  • -Wconversion : 针对可能改变 value*的隐式转换发出警告。
  • -Wunreachable-code : 如果编译器检测到代码将永远不会被执行,则发出警告。

那些被标记的 *有时会给出太多虚假的警告,所以我根据需要使用它们。

- March = 原生 为您正在编译的平台(= chip)生成优化的代码。

- Wfloat-平分

来自 Char * const argv [] :

我喜欢的另一个新警告是-Wfloat-equals。当你在等式条件下有一个浮点数时,它会发出警告。太聪明了!如果你已经编写了一个计算机图形学或者(更糟糕的是)计算几何算法,你就会知道没有任何两个浮点数可以匹配等式... ..。

如果需要知道编译器预定义的预处理器标志:

echo | gcc -E -dM -

- fmudflap ーー为所有有风险的指针操作添加运行时检查,以捕捉 UB。这有效地防止了程序再次出现缓冲区溢出,并有助于捕获各种悬空指针。

参见 译自: 美国《科学》杂志网站(http://gcc.gnu.org/wiki/MudflapPointer _ Debug)

下面是一个演示:

文件 MFC

int main()
{
int a[10];
a[10] = 1; // <-- Oh noes, line 4
}

编译和执行

gcc -fmudflap mf.c -lmudflap
./a.out

产出:

*******
mudflap violation 1 (check/write): time=1280862302.170759 ptr=0x7fff96eb3d00 size=44
pc=0x7f3a575503c1 location=`mf.c:4:2 (main)'
/usr/lib/libmudflap.so.0(__mf_check+0x41) [0x7f3a575503c1]
./a.out(main+0x90) [0x400a54]
/lib/libc.so.6(__libc_start_main+0xfd) [0x7f3a571e2c4d]
Nearby object 1: checked region begins 0B into and ends 4B after
mudflap object 0xf9c560: name=`mf.c:3:6 (main) a'
bounds=[0x7fff96eb3d00,0x7fff96eb3d27] size=40 area=stack check=0r/3w liveness=3
alloc time=1280862302.170749 pc=0x7f3a57550cb1
number of nearby objects: 1

-save-temps

这就留下了预处理器和程序集的结果。

预处理源对于调试宏非常有用。

该程序集对于确定哪些优化生效很有用。例如,您可能希望验证 GCC 正在对某些递归函数执行尾部调用优化,因为如果没有它,您可能会溢出堆栈。

它对于检测错误并没有真正的帮助,但是很少提到的 MASM = 英特尔选项使得使用 -S来检查组装输出要好得多。

AT & T 汇编语法太让我头疼了。

虽然这个答案可能有点跑题,问题是一个值得从我 + 1,因为

我特别感兴趣的是任何额外的警告,以及/或在某些情况下将警告转换为错误,以绝对减少任何意外的类型不匹配。

有一个工具,应该捕获 强大错误和潜在的错误,可能是不明显的。恕我直言,与 GCC 或任何其他编译器相比,夹板在发现错误方面做得更好。这是一个有价值的工具,在你的工具箱。

通过像 Splint 这样的 lint 类型的工具进行静态检查,应该是编译器工具链的一部分。

我的 makefile 通常包含

CFLAGS= -Wall -Wextra -Weffc++ -Os -ggdb
...
g++ $(CFLAGS) -o junk $<
gcc $(CFLAGS) -o $@ $<
rm -f junk

这些选项中最重要的已经讨论过了,所以我将指出两个尚未指出的特性:

尽管我正在开发一个代码库,使得 需求成为纯 C,以便能够移植到某些 还是没有像样的 C + + 编译器的平台上,我还是用 C + + 编译器(除了 C 编译器之外)做了一个“额外的”编译。这有三个好处:

  1. C + + 编译器偶尔会给我比 C 编译器更好的警告信息。
  2. C + + 编译器接受 - Weffc + + 选项,它偶尔会给我一些有用的提示,如果我只用普通 C 编译它,我就会错过这些提示。
  3. 我可以让代码相对容易地移植到 C + + ,避免一些普通 C 代码无效的边界条件(比如定义一个名为“ bool”的变量)。

是的,我是一个无可救药的乐观主义者,我一直认为 当然随时都可能过时,因为一个平台要么会被宣布过时,要么会得到一个像样的 C + + 编译器,我们最终可以切换到 C + + 。在我看来,这是不可避免的ーー唯一的问题是,这种情况是在管理层最终给每个人发放小马驹之前还是之后发生的。:-)

这里有一面没有被提及的伟大旗帜:

-Werror-implicit-function-declaration

在声明函数之前,只要使用了函数,就给出一个错误。

-M*系列选择。

这样就可以编写 make 文件,自动计算出 C 或 C + + 源文件应该依赖哪些头文件。GCC 将使用此依赖项信息生成 make 文件,然后从主 make 文件中包含它们。

下面是一个使用 医学博士宪兵的非常通用的 makefile 示例,它将编译一个包含 C + + 源文件和头文件的目录,并自动计算出所有的依赖项:

CPPFLAGS += -MD -MP
SRC = $(wildcard *.cpp)


my_executable: $(SRC:%.cpp=%.o)
g++ $(LDFLAGS) -o $@ $^


-include $(SRC:%.cpp=%.d)

下面是一篇更深入讨论它的博客文章: 自动生成 makefile 依赖项

我发现这个问题寻找一个标志来修复一个特定的问题。我在这里没有看到,所以我要加一个刚刚在 我的岗位上难倒我的:

- Wformat = 2旗帜

检查对 printfscanf等的调用,以确保提供的参数具有与指定的格式字符串相适应的类型..。

关于它最重要的部分(根据海湾合作委员会手册) :

-Wformat包含在 -Wall中。为了对格式检查的某些方面进行更多的控制,可以使用 -Wformat-y2k-Wno-format-extra-args-Wno-format-zero-length-Wformat-nonliteral-Wformat-security-Wformat=2选项,但是不包括在-Wall 中。`

所以,仅仅因为你有 并不意味着你拥有一切。 ;)

它与 C/C + + 并无实际关系,但无论如何它是有用的:

@file

将以前答案中的所有好标志(你们都已经指定了)放在一个“文件”中,并使用上面的标志将该文件中的所有标志一起使用。

例如:

文件编译器标志

- std = c99

- 是的

然后编译:

gcc yourSourceFile @compilerFlags
  • - Wmiss-Prototype : 如果定义的全局函数没有先前的原型声明。
  • - Wformat-security : 警告使用表示可能的安全问题的格式函数。目前,对于格式字符串不是字符串文字且没有格式参数的 printfscanf函数的调用,这将发出警告

事实上,我想让我的 C 代码跨平台。我使用 CMake,并将提供的 cFlag 放入 CMakeLists.txt,如下所示:

if (CMAKE_SYSTEM_NAME MATCHES "Windows")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /we4013 /we4133 /we4716")
elseif (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Darwin")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=implicit-function-declaration -Werror=incompatible-pointer-types -Werror=return-type")
endif()