编译器:如果条件始终为真/假,会发生什么?

13

我在思考条件语句和编译器。我正在为Arduino编写应用程序,因此需要尽可能使应用程序更快。

在我的代码中有这个:

#define DEBUG false    

...

if (DEBUG)
{
  String pinName;
  pinName = "Pin ";
  pinName += pin;
  pinName += " initialized";
  Serial.println(pinName);
}

我在想编译器是否不会将代码(if块中的代码)包含在二进制文件中。条件始终为false,因此程序从不执行该部分。

从另一方面来看,如果DEBUG为true,Arduino会测试条件还是编译器仅将if体包含在二进制文件中?

我找到了这个网站https://gcc.gnu.org/onlinedocs/gcc-3.0.2/cpp_4.html关于#if指令,所以我可以重写代码,使用这些指令代替“常规”的if。但我想知道是否应该重写它,或者这将是浪费时间。


3
智能编译器会进行优化,如果DEBUG为假,则不会在二进制代码中包含该代码。 - The Philomath
7
“如果(CONSTANT) { ... }”对于编译器来说非常容易进行优化。我预计过去25年中至少任何一款像样的编译器都能够删除条件分支。 - Phylogenesis
4
即使在二进制文件中没有任何区别,我仍然会使用 #if/#endif。这样可以清楚地表明 DEBUG 的含义。 - LogicStuff
3
使用#if/#endif是一个糟糕的主意,因为如果条件为假,编译器将不会检查代码。我们知道任何真实世界的编译器都会像#if/#endif一样优化代码,所以我倾向于不使用宏。 - Klaus
我需要应用程序尽可能快。请使用汇编语言编写,而不是C/C++。 - Aleksander Z.
1
@AleksanderZ. 好的,评论不错。但我还需要WiFi控制和HTTP通信。而且我希望在这个有生之年内完成它 :)。 - Martin Pohorský
3个回答

10

任何半靠谱的优化编译器都会在编译期间检测到条件始终为假时,删除 if 语句内的整个代码。同样地,如果条件始终为真,任何半靠谱的编译器都会跳过检查本身。

实际上,这完全等同于“编译器开关”:

#define DEBUG


#ifdef DEBUG
...
#endif

使用#ifdef的“编译器开关”语法更受其他C程序员的欢迎,因为它使意图更清晰。但这只是一种编码风格问题——它将产生与原始代码相同的二进制文件。


1
你编写的代码不应该被执行,但它可能在可执行文件中存在。
我认为当你禁用优化(例如在Clang和GCC中添加-O0)时,编译器会保留此代码。 在其他所有情况下,我希望编译器会删除此代码,因为这是一个非常简单的优化,具有显着的代码大小效果。例如,GCC在-O及更高级别下会消除此优化。(请参见手册
然而,还有两种编写代码的方式可以强制不包含此代码:
  • 预处理器条件
  • constexpr if
通过使用预处理器条件,您可以在代码到达实际编译器之前将其删除。虽然它可能会对您的缩进有所影响,但它将适用于C和C++标准的所有编译器和版本。
#ifdef DEBUG
   { // Optional: Adding extra scope to prevent usage of local variables after the endif
   // Code to eliminate
   }
#endif

然而,如果您正在使用C++17,您也可以使用constexpr if。这将对您的代码影响较小,尽管if语句中的代码必须是语法正确的,但它不必编译(因此不需要语义正确)。
if constexpr (DEBUG)
{
    // Code to eliminate
}

它是if constexpr吗? - RiaD
不,这是“constexpr if”。我在回复中添加了提案链接。 - JVApen
我认为这已经过时了 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0292r1.html https://herbsutter.com/2016/06/30/trip-report-summer-iso-c-standards-meeting-oulu/ - RiaD

0

我不喜欢回答自己的问题,因为没有你们的帮助,我可能想不出来。

无论如何,第一种选择是使用:

#if DEBUG == true
#endif

或者

#ifdef DEBUG
#endif

编译器无法获取#if / #ifdef中的代码(预处理器会将其删除),因此如果在代码的这部分存在问题,并且DEBUG设置为false或根本未定义,那么没有人会知道它的存在(感谢@Klaus)。

第二个选项:

#define DEBUG false    

...

if (DEBUG)
{
  ...
}

任何更新的编译器都应该在条件为假时删除“if”块或者删除“if”语句,只保留代码主体。
所以我认为程序员应该根据应用的需要选择最好的方法。
如果你需要确保代码不会包含在二进制文件中,那么第一种方法更好。
如果你希望编译器每次编译程序时都检查整个代码,则第二种方法更好。
如果你喜欢这个答案,请投票支持,我会接受它——如果没有人提供更好的答案的话。

#if DEBUG == true 可以被替换成 #if DEBUG。另一个选项是 #ifdef DEBUG。对于一些像库选项这样的定义,你可以使用 #if 风格,但特别针对 DEBUG,最好使用 #ifdef 语法。 - i486

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接