3

我正在查看其他人编写的代码,它有很多调试部分,类型

if(0) { code }

或者if(1) { code }

或者if(false) { code }

甚至还有

#if(0)
#endif

(虽然它没有变灰 - 我认为它应该)

#if 0我想知道,如果我用一些(或)替换这些#ifdef _DEBUG,是否可以优化代码?- 或者 - 它不会有任何区别?

我认为这可能会有所帮助,因为我已经看到这些部分中的代码变灰了 - 我认为该代码已从 Release 可执行文件中删除......因此使其更快。真的吗 ?

我正在考虑的代码在可以被多次调用的函数内部......

编辑:我所指的代码正在运行数百万次。我知道 if(0) 的内容将被忽略...

我也意识到能够通过将测试从 0 切换到 1 来轻松调试问题的好处......

我的问题是,我添加数百万次测试 if(0) 并没有增加开销的事实......我试图找出可以使这段代码花费更少时间的所有事情。

4

5 回答 5

2

如果放置在这些 IF 中的表达式在编译时是常量且可确定的,那么您几乎可以肯定编译器已经为您从代码中删除了它们。

当然,如果您在调试模式下编译,和/或如果您将优化级别设置为零,那么编译器可能会跳过它并离开这些测试 - 但对于简单的零/一/真/假值,这是极不可能的.

对于编译时常量分支,您可以确定编译器删除了死分支。

它甚至可以删除看起来很复杂的情况,例如:

const int x = 5;

if( 3 * x * x < 10 ) // ~ 75 < 10
{
    doBlah(); // skipped
}

但是,如果 X 处没有那个 'const' 标记,表达式的值在编译时可能无法确定,它可能会“泄漏”到实际的最终产品中。

此外,以下代码中的表达式值不一定是编译时常量:

const int x = aFunction();

if( 3 * x * x < 10 ) // ~ 75 < 10
{
    doBlah(); // skipped
}

X 是一个常数,但它是用函数的值初始化的。X 很可能在编译时无法确定。在运行时,函数可以返回任何值*),因此编译器必须假定 X 是未知的。

因此,如果有可能,请使用预处理器。在微不足道的情况下不会做太多,因为编译器已经知道了。但案例并不总是微不足道的,您会经常注意到变化。当优化器无法推断出这些值时,它会留下代码,即使它已经死了。另一方面,预处理器保证在编译和优化之前删除禁用的部分。此外,使用预处理器至少会加快编译速度:编译器/优化器不必跟踪常量/计算/检查分支等。

*) 可以编写一个方法/函数,它的返回值实际上在编译和优化阶段是可确定的:如果函数很简单并且被内联,它的结果值可能会与一些分支一起被优化。但是即使您可以在某种程度上依赖于删除 if-0 子句,也不能过多地依赖内联。

于 2013-02-02T01:23:19.700 回答
2

如果您在if (0)块内有代码,则编译器生成的代码将与该块在任何合理的编译器上不存在一样。仍将检查代码是否存在编译时错误。(假设你里面没有任何跳转标签或类似的奇怪东西。)

如果代码if (1)块内有代码,则编译器生成的代码将与代码仅在大括号内一样。这是为代码块提供自己的范围的常用方法,以便在需要的地方破坏局部变量。

如果您ifdef输出代码,则编译器会完全忽略它。代码可能完全是胡说八道,包含语法错误或其他任何东西,编译器不会关心。

于 2013-02-02T01:24:10.593 回答
2

通常,#if 0用于删除代码同时仍然保留它 - 例如,为了轻松与选项进行比较,我有时会这样做:

#if 1
   some sort of code 
#else
   some other code
#endif

这样,我可以在两种选择之间快速切换。

在这种情况下,预处理器只会在代码中保留两个选项之一。

if(0)or的结构if(1)类似 - 编译器几乎会删除 if,并且在 0 的情况下也会删除 if 语句的其余部分。

我认为将这类东西留在“已完成”的代码中是相当草率的,但它对于调试/开发非常有用。

例如,假设您正在尝试一种新方法来做更快的事情:

if (1) 
{
   fast_function();
}
else
{
    slower_function();
}

现在,在您的一个测试用例中,结果显示错误。因此,您想快速返回slower_funcion并查看结果是否相同。如果相同,那么您必须查看自上次通过以来发生的其他变化。如果使用较慢的函数没问题,则返回并查看为什么 fast_function() 在这种情况下不能正常工作。

于 2013-02-02T01:27:02.230 回答
1

这是真的(取决于您的构建设置和预处理器)。

将调试代码放入#ifdef _DEBUG(或类似的)是一种标准方法,可以将这些完全排除在您的发布版本之外。通常调试版本#define是它,而发布版本没有。

但是,通常情况下,如果给定适当的优化标志,编译器也应该删除诸如 之类的代码if (0),但这会给编译器和程序员带来额外的工作(现在您必须全部更改!)。我肯定会把它留给预处理器。

于 2013-02-02T01:21:55.753 回答
1

你是对的。如果你编译,#define DEBUG 0那么你实际上将#if DEBUG在编译时删除所有块。因此,将有更少的代码,它会运行得更快。

只需确保#define DEBUG 0在发布时发布代码即可。

一个好的优化编译器(GCC,MSVC)将完全删除if(0)if(1)从代码中删除......转换为机器代码不会测试这些条件......

于 2013-02-02T01:25:22.093 回答