编译器的“-O0”选项和“-Og”选项有什么区别?

Gab*_*les 10 c c++ debugging gdb compiler-optimization

当我想要调试 C 或 C++ 程序时,我被教导如何使用-O0关闭优化,并将-ggdb符号插入到可执行文件中,这些符号针对使用gdb我使用的 GNU 调试器进行了优化(或者,您可以-glldb使用LLVM/clang 的lldb调试器,或者只是-g用于一般调试符号,但这不会像-ggdb表面上那么好......)。然而,我最近偶然发现有人说使用-Og(而不是-O0),这让我措手不及。果然,它在man gcc!:

-Og优化调试体验。 -Og启用不干扰调试的优化。它应该是标准编辑-编译-调试周期的优化级别选择,提供合理的优化级别,同时保持快速编译和良好的调试体验。

那么,有什么区别呢?这是-O0来自的描述man gcc

-O0减少编译时间并使调试产生预期结果。这是默认设置。

man gcc不过,明确表示-Og“应该是标准编辑-编译-调试周期选择的优化级别”。

这听起来像是-O0真正的“没有优化”,而-Og“进行了一些优化,但只是那些不干扰调试的优化”。它是否正确?那么,我应该使用哪个,为什么?

有关的:

  1. 相关,但不重复!(仔细阅读,它根本不重复):-O0 、-O1 和 -g 之间有什么区别
  2. 我对--copt=与 Bazel 一起使用的调试设置的回答:gdb:当前上下文中没有符号“i”

Gab*_*les 13

快速总结

不使用。中断调试。-Og-Og

使用-ggdb -O0(如果使用gdb调试器则首选),或者-g3 -O0代替。

使用-g -O0也可以,但-g单独使用默认为调试级别 2 ( -g2),这意味着与 相比-g3-g缺少“额外信息,例如程序中存在的所有宏定义”。(查看man gcc并搜索-glevel)。

细节

@kaylum 刚刚在我的问题下的评论中提供了一些很棒的见解!我最关心的关键部分是:

对于生成可调试代码, [ -Og] 是比 -O0 更好的选择,因为在 -O0 处禁用了一些收集调试信息的编译器传递。

https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Optimize-Options

所以,从现在开始,-Og除了.-O0-ggdb


UDP日期 2020 年 8 月 13 日:

见鬼!没关系。我坚持使用-O0.

-Og我得到了到处都是错误<optimized out>Can't take address of "var" which isn't an lvalue.我无法再打印我的变量或检查它们的内存!前任:

(gdb) print &angle
Can't take address of "angle" which isn't an lvalue.
(gdb) print angle_fixed_p
$6 = <optimized out>
Run Code Online (Sandbox Code Playgroud)

然而,有了-O0,一切都正常!

(gdb) print angle
$7 = -1.34869879e+20
(gdb) print &angle
$8 = (float *) 0x7ffffffefbbc
(gdb) x angle
0x8000000000000000:     Cannot access memory at address 0x8000000000000000
(gdb) x &angle
0x7ffffffefbbc: 0xe0e9f642
Run Code Online (Sandbox Code Playgroud)

所以,回到使用它-O0来代替-Og它!

有关的:

  1. [他们也推荐-O0,我也同意] gdb 中 <value optimization out> 是什么意思?

  • 我还推荐使用“-O0”进行调试。使用“-Og”断点往往会在错误的行处触发。 (5认同)
  • 是的,“-Og”代码生成更像“-O1”,而不是通过“-O0”获得的真正严格的调试支持。([为什么 clang 使用 -O0 生成低效的 asm(对于这个简单的浮点和)?](/sf/ask/3735647611/))。`-Og` 让编译器优化掉本地临时变量。您可以通过“gcc -Og -S -fverbose-asm”看到其中的一些内容,在寄存器上操作时,它仍然为它发明的临时变量发明名称,而不是使用原始的 C var 名称。尽管在简单的情况下,“-Og”看起来比“-O1”更像 C 源代码:https://godbolt.org/z/zxcqb66jx (2认同)