如果在C程序中编译了0

3

我在尝试从我的程序中移除未使用的代码 - 我现在不能删除代码,我只想先禁用它。

假设我有以下代码:

if (cond){
  doSomething()
}

并且cond永远为假,因此doSomething从未被调用。

我想做的事情类似于:

#define REMOVE_UNUSED_CODE 0
if (cond && REMOVE_UNUSED_CODE){
  doSomething()
}

现在对我们来说(希望编译器也是如此),这段代码已经没用了。
编译器会删除所有的if条件语句还是会留下来但永远不会执行呢?
附注:我不能使用#if 0来实现这个目的。

cond 将在运行时被评估,你如何期望在编译时就知道它呢? - Sadique
我不确定编译器的标准,但编译器应该能够识别REMOVE_UNUSED_CODE并在启用适当优化的情况下将死代码剔除。虽然不是很确定。 - Sourav Ghosh
1
你能不能试一下呢?此外,如果你连使用哪个编译器(以及哪个优化级别)都不告诉我们,那么也没有人能告诉你“the”编译器会做什么。我非常有信心地认为,大多数启用了优化的编译器都能够弄清楚 x && false == false - 假设 cond 可以轻松确定没有副作用。 - sepp2k
这显然是为一个安全关键系统(如下所述)而设计的,这一点很重要,应该在问题中提到 - 安全/可靠的代码通常不允许像“普通”代码那样进行优化,因为更高级的编译器优化有时会引入与最初编写的逻辑不同的逻辑(我认为这并不改变对这个具体问题的实际答案 - 这是一个非常基本的优化,第一层 - 但这是一个非常重要的事情,需要在一般情况下注意)。 - Alex Celeste
我宁愿写if (REMOVE_UNUSED_CODE && cond),因为C语言中的布尔运算符总是短路求值,这样可以避免可能出现的cond副作用。 - rodrigo
显示剩余3条评论
6个回答

3
GCC将明确删除具有控制它们的常量表达式的条件块,GNU编码标准明确建议您利用此功能,而不是使用更粗糙的方法,如依赖预处理器。虽然使用预处理器#if可能看起来更有效率,因为它在较早的阶段删除代码,但在实践中,现代优化器在尽可能提供更多信息时效果最好(当然,如果您可以避免在不必要的地方添加阶段分离依赖,则会使程序更加清洁和一致)。
像这样的决定对于现代编译器来说非常容易 - 即使非常简单的TCC也将执行某些死代码消除; 强大的系统如LLVM和GCC将发现这一点,以及您作为人类读者可能会错过的更复杂的情况(通过跟踪变量的生命周期和修改超出单个点的范围)。
这是完全编译的其他优势之一,例如您在 if 中的代码将被检查错误(而 #if 更适用于删除在当前系统上可能出错的代码,例如对不存在平台函数的引用)。

2

尝试

#ifndef REMOVE_UNUSED_CODE
if (cond) {
   doSomething();
}
#endif

请注意,不要忘记执行

#define REMOVE_UNUSED_CODE

某个地方。


1
答案取决于编译器的实现。您不应该依赖这一点。相反,要明确地表达:
#define REMOVE_UNUSED_CODE 0
if (cond && REMOVE_UNUSED_CODE){
#ifndef REMOVE_UNUSED_CODE  
   doSomething()
#endif
}

1
我不认为这会取决于编译器。它必须是一个相当愚蠢的编译器才会优化掉未使用的代码。我不认为市场上存在这样糟糕的编译器。 - Lundin
1
我同意,我也会这样假设。但是为了绝对确定,更安全的方式是显式表达。如果结果关系到生死,你不会做出那种假设。 - Phil Williams
1
如果结果关系到生死(安全关键应用),那么就有许多标准明确禁止使用此类构造。在安全关键系统中,你不允许有任何永远不会被执行的代码。 - Lundin
这里的情况是这样的。这是一个安全关键系统,我正在进行覆盖测试以消除所有未执行的代码。第一步是禁用所有这些代码,然后重新运行主要测试,以确保没有发现性能问题。 - Shai Zarzewski
如果你想确定,只需查看目标代码。使用将汇编与C代码注释的开关。 - Phil Williams

0

看一下你代码片段

#define REMOVE_UNUSED_CODE 0

if(cond && REMOVE_UNUSED_CODE)
{
    doSomething();
}

REMOVE_UNUSED_CODE 在编译之前被预处理器替换为 0。 因此,if(cond && 0) 将始终被评估为 false,并且永远不会被执行。因此,无论 cond 是什么,它都将始终为 false。

所以我更喜欢这样做:

//#define REMOVE_UNUSED_CODE 0

#ifdef REMOVE_UNUSED_CODE
    if(cond)
    {
        doSomething();
    }
#endif

0

虽然不是直接回答,但可能会有所帮助。有编译器特定的内置函数可以进行代码删除。

对于 MSVC,它是 __assume(0)

对于 GCC/Clang,它是 __builtin_unreachable()

请注意,如果您编写类似以下的内容:

if (cond){
  __builtin_unreachable();
  doSomething()
}

你的条件不应该评估为true,否则行为是未定义的。 同时结合使用&& REMOVE_UNUSED_CODE__builtin_unreachable()可以确保编译器将删除你的代码。


0

你不应该那样做:

假设你有一个带有副作用的函数bool f(void* input)

然后使用

if( f(pointer) && false ) { ... }

正文不被评估,但应该评估f

在这种情况下

if( false && f(pointer) ) { ... }

由于逻辑运算符 && 的短路规则,f 没有被求值。

如果您使用

#define REMOVE_UNUSED_CODE
#ifndef REMOVE_UNUSED_CODE
if( f(pointer) && false ) { }

#endif

整个代码甚至没有编译,也永远不会被执行。


快捷评估仅适用于C++,而不适用于C。 - Phil Williams
1
@PhilWilliams,这篇文章和维基百科(引用标准)都表示不同意。 - martin
抱歉,你是正确的。我不确定我从哪里得到这个想法? - Phil Williams

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