【问题标题】:What are the useful GCC flags for C?C 的有用 GCC 标志是什么?
【发布时间】:2011-03-23 11:41:03
【问题描述】:

除了设置 -Wall 和设置 -std=XXX,还有哪些其他真正有用但鲜为人知的编译器标志可用于 C 语言?

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

【问题讨论】:

  • 好吧,-save-temps-Wshadow-fmudflap 是我不知道的最伟大的发现,感谢所有人。
  • 上下文,据我所知:运行gcc -c [flags-go-here] -o myprog.o myprog.c 来编译(而不是链接)C 程序。

标签: c gcc compiler-flags


【解决方案1】:

-f 的几个代码生成选项很有趣:

  • 如果您使用-S 编译以检查程序集输出,-fverbose-asm 很有用 - 它添加了一些信息丰富的 cmets。

  • -finstrument-functions 添加代码以在每个函数入口和出口点调用用户提供的分析函数。

  • --coverage 检测程序中的分支和调用,并创建覆盖注释文件,以便在程序运行时生成覆盖数据,该数据可由gcov 程序格式化以帮助分析测试覆盖。

  • -fsanitize={address,thread,undefined} 分别启用 AddressSanitizer、ThreadSanitizer 和 UndefinedBehaviorSanitizer 代码清理程序。这些检测程序以在运行时检查各种错误。

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

【讨论】:

  • 对于-ftrapv,请看这里stackoverflow.com/questions/20851061/… ..似乎有一个等待修复的错误。
  • 你能查看上面的评论吗?
  • -ftrapv 基本上被 -fsanitize=signed-integer-overflow 取代。
【解决方案2】:
  • -Werror=return-type:当函数在 gcc 中没有返回时强制错误。在 Visual Studio 中是 /we4716

  • -Werror=implicit-function-declaration:在使用未定义/未包含的函数时执行错误。在 Visual Studio 中是 /we4013

  • -Werror=incompatible-pointer-types:当指针的类型与预期的指针类型不匹配时出现错误。在 Visual Studio 中是 /we4133

实际上,我想保持我的 C 代码跨平台,并且我使用 CMake,并将提供的 cflags 放入 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()

【讨论】:

    【解决方案3】:
    • -Wmissing-prototypes:如果定义了全局函数而没有先前的原型声明。
    • -Wformat-security:警告格式函数的使用代表可能的安全问题。目前,这会警告对 printfscanf 函数的调用,其中格式字符串不是字符串文字并且没有格式参数

    【讨论】:

      【解决方案4】:

      -Werror,它将所有警告视为错误并停止编译。 gcc manual page 解释了编译器的每个命令行开关。

      【讨论】:

      • @Matt Joiner:由于您没有提及您使用的机器架构,gcc 标志可能与您的标志和任何人可能建议的链接不同。这就是您的软件随附手册页的原因。
      【解决方案5】:

      与 C/C++ 无关,但无论如何都很有用:

      @file
      

      将上述所有好的标志(你们都指定)放在一个“文件”中,并使用上面的这个标志来一起使用该文件中的所有标志。

      例如:

      文件:编译器标志

      -墙

      -std=c99

      -Wextra

      然后编译:

      gcc yourSourceFile @compilerFlags
      

      【讨论】:

        【解决方案6】:

        我发现这个帖子正在寻找一个标志来解决特定问题,我在这里看不到它,所以我会添加一个让我在my post 上难倒我的帖子:

        -Wformat=2 标志

        -Wformat => 检查对printfscanf 等的调用,以确保提供的参数具有适合指定格式字符串的类型...

        关于它的真正重要的部分 (according to the GCC manual):

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

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

        【讨论】:

          【解决方案7】:

          -Wfloat-equal

          发件人:http://mces.blogspot.com/2005/07/char-const-argv.html

          我喜欢的其他新警告之一是 -Wfloat-equal。每当您在相等条件下[有]一个浮点数时,都会发出警告。这太棒了!如果你每个人都编写了计算机图形或(更糟糕的:)计算几何算法,你就会知道没有两个浮点数匹配相等......

          【讨论】:

          • 我的花车 do 匹配相等,因为我知道自己在做什么。
          【解决方案8】:
          -save-temps
          

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

          预处理的源代码对于调试宏很有用。

          程序集可用于确定哪些优化已生效。例如,您可能想要验证 GCC 是否正在对某些递归函数进行尾调用优化,因为没有它您可能会溢出堆栈。

          【讨论】:

          • 我确实想知道你是如何做到这一点的......我一直只是要求 gcc 在我需要时转储程序集。
          【解决方案9】:

          -M* 系列选项。

          这些使您可以编写自动确定您的 c 或 c++ 源文件应该依赖的头文件的 make 文件。 GCC 将使用此依赖信息生成 make 文件,然后您从主 make 文件中 -include 它们。

          这是一个使用 -MD 和 -MP 的极其通用的 makefile 示例,它将编译一个充满 c++ 源文件和头文件的目录,并自动找出所有依赖项:

          CPPFLAGS += -MD -MP                                         
          SRC = $(wildcard *.cpp)                                                       
          
          my_executable: $(SRC:%.cpp=%.o)                                                        
                  g++ $(LDFLAGS) -o $@ $^                                               
          
          -include $(SRC:%.cpp=%.d)
          

          这是一篇更深入地讨论它的博文:http://www.microhowto.info/howto/automatically_generate_makefile_dependencies.html

          【讨论】:

            【解决方案10】:

            这是一个没有被提及的伟大旗帜:

            -Werror-implicit-function-declaration
            

            在声明之前使用函数时给出错误。

            【讨论】:

              【解决方案11】:

              我的 makefile 通常包含

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

              其中最重要的选项之前已经讨论过,所以我将指出尚未指出的两个功能:

              即使我正在开发一个 需要 纯 C 的代码库,以便移植到一些 仍然 没有像样 C++ 编译器的平台,我还是做了一个“额外的" 用 C++ 编译器编译(除了 C 编译器)。这有 3 个好处:

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

              是的,我是一个绝望乐观的 Pollyanna,她一直认为肯定现在任何一个月都有一个平台要么被宣布过时,要么获得一个像样的 C++ 编译器,我们终于可以切换到 C++ .在我看来,这是不可避免的——唯一的问题是这发生在管理层最终给每个人发小马之前还是之后。 :-)

              【讨论】:

              • 将其编写为 C++ 是一个好点,我经常考虑这一点。 (自然子集)
              • 我应该指出,C 被弃用以支持 C++ 永远不会发生,抱歉 :)
              • 考虑 -o /dev/null 而不是 rm -f junk
              【解决方案12】:

              虽然这个答案可能有点离题,而且这个问题值得我 +1,因为

              我对任何额外的警告特别感兴趣,和/或在某些情况下将警告转化为错误以绝对减少任何意外的类型不匹配。
              有一个工具可以找出ALL 错误和可能不明显的潜在错误,有 splint 恕我直言,与 gcc 或任何其他编译器相比,它在发现错误方面做得更好。这是您工具箱中值得拥有的工具。

              通过 lint 类型的工具(如夹板)进行静态检查应该是编译器工具链的一部分。

              【讨论】:

              • 它总是显示一个错误 cannot file preprocessor file in C:\include ,我不知道该怎么办
              【解决方案13】:

              这对于检测错误并没有真正的帮助,但是很少提到的-masm=intel 选项使得使用-S 来检查程序集输出要好得多。

              AT&T 汇编语法太伤我的头了。

              【讨论】:

              • 在我看来,AT&T 和 Intel 的区别就是 C# 和 Java 的区别。只是语法。两者都很糟糕。 :)
              • +1 @michael 让 gcc 使用 intel 语法,而不是可怕的 at&t。检查程序集使用了足够的大脑周期——无需浪费 src 在操作码中位于 dest 之前的大脑周期。现在,如果只有 gcc 像其他编译器一样支持 __asm {} 内联,我们就大功告成了!
              【解决方案14】:

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

              这是一个演示:

              $ cat mf.c 
              int main()
              {
               int a[10];
               a[10]=1; // <-- o 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
              

              【讨论】:

              • 嗯,挡泥板看起来很讨厌:P
              • -fmudflap 自 GCC 4.9 起不再受支持,您将获得 warning: switch '-fmudflap' is no longer supported。它已被 AddressSanitizer 取代。
              【解决方案15】:

              如果您需要了解编译器预定义的预处理器标志:

              echo | gcc -E -dM -
              

              【讨论】:

                【解决方案16】:

                -march=native 为您正在编译的平台(=芯片)生成优化代码

                【讨论】:

                • 如果您正在为不知道目标的非本机机器进行编译,您可以使用 mtune=xxx 进行优化而不使用指令集。例如,mtune=generic 与“平均”案例处理器保持同步。
                【解决方案17】:

                这是我的:

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

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

                【讨论】:

                • 很完整的列表,只想多加一个; -Wformat=2: printf/scanf 函数的额外格式检查。
                • 不是-Wall暗示的所有这些吗?
                • @chacham15,不,我不这么认为。 gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
                • @Alok 嗯,也许它不是发行版的标准?我知道在我的 mbp 上我必须明确关闭 -Wwrite-strings 因为我非常讨厌它。
                • @chacham15,也许吧。但是-Wwrite-strings 的描述明确表示它不是-Wall 的一部分:gcc.gnu.org/onlinedocs/gcc/…。也许您设置中的其他东西正在设置该标志?或者你正在编译 C++?
                【解决方案18】:

                我有时将-s 用于更小的可执行文件:

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

                来源:http://gcc.gnu.org/onlinedocs/gcc/Link-Options.html#Link-Options

                【讨论】:

                • 你应该在你的二进制文件上运行strip,这样你就可以得到一个带有调试信息的二进制文件,稍后将其删除以进行分发。
                • 是的,strip 也可以,但-s 可以更快更容易,虽然它不像运行strip 那样复杂
                【解决方案19】:

                我对任何其他警告特别感兴趣,

                除了-Wall-W-Wextra 选项(-W 可用于旧版本的 gcc 以及较新的 gcc;较新的版本支持替代名称 -Wextra,这意味着相同东西,但更具描述性)启用各种附加警告。

                还有更多警告,这些警告均未启用,通常是针对更可疑的坏事。可用选项集取决于您使用的 gcc 版本 - 请咨询 man gccinfo gcc 了解详细信息,或查看 online documentation 了解您感兴趣的特定 gcc 版本。-pedantic 会发出所有警告所使用的特定标准所要求的(取决于其他选项,例如 -std=xxx-ansi)并抱怨使用 gcc 扩展。

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

                -Werror 将所有警告变成错误。不过,我认为 gcc 不允许您有选择地针对特定警告执行此操作。

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

                【讨论】:

                • “不过,我认为 gcc 不允许您有选择地针对特定警告执行此操作。”实际上,您可以使用-Werror=some-warning
                【解决方案20】:
                -Wstrict-prototypes -Wmissing-prototypes
                

                【讨论】:

                • 还有-Wold-style-definition,如果你不得不与那些认为 K&R 风格函数是个好主意的惯犯打交道,即使是原型声明也是如此。 (我必须与这样的人打交道。当我发现用 K&R 编写的新代码时,我真的很烦。拥有未修复但新代码的遗留 K&R 东西已经够糟糕了!抱怨!!!)
                【解决方案21】:

                始终使用-O 或更高版本(-O1-O2-Os 等)。在默认优化级别,gcc 会提高编译速度,并且不会进行足够的分析来警告诸如单元化变量之类的事情。

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

                -Wall 几乎会打开很可能是错误的警告。

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

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

                -Winit-self 很有用;我想知道为什么它没有包含在-Wuninitialized 中。

                -Wpointer-arith 如果您的大部分可移植代码不适用于-pedantic,则很有用。

                【讨论】:

                  【解决方案22】:

                  我很惊讶没有人这么说 - 就我而言,最有用的标志是 -g 它将调试信息放入可执行文件中,以便您可以调试它并单步调试源代码(除非您精通和阅读程序集,喜欢程序的stepi 命令),同时它正在执行。

                  【讨论】:

                    【解决方案23】:

                    嗯,-Wextra 也应该是标准的。 -Werror 将警告变成错误(这可能非常 很烦人,尤其是如果您在没有-Wno-unused-result 的情况下进行编译)。如果您使用 C99 功能,-pedanticstd=c89 结合使用会给您额外的警告。

                    但仅此而已。您无法将 C 编译器调整为比 C 本身更节省类型的东西。

                    【讨论】:

                      【解决方案24】:
                      man gcc
                      

                      该手册充满了有趣的标志和良好的描述。但是, -Wall 可能会使 gcc 尽可能冗长。如果你想要更多有趣的数据,你应该看看 valgrind 或其他一些检查错误的工具。

                      【讨论】:

                      • 不过,它是 looooooooooooooooooooooooooooooong。 man gcc | nl 报告超过 11000 行。这不仅仅是臭名昭著的 bash 联机帮助页!
                      • 感谢上帝,他们把它塞进了一个手册页,而不是那些可怕的无法导航的“信息”页面之一。
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2023-03-10
                      • 2019-10-26
                      • 2022-01-14
                      • 1970-01-01
                      • 2018-05-21
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多