【问题标题】:if 0 is compiled in c program如果在c程序中编译为0
【发布时间】:2015-07-23 04:14:30
【问题描述】:

我正在尝试从我的程序中删除未使用的代码 - 我现在无法删除该代码,我只想先禁用它。

假设我有以下代码:

if (cond){
  doSomething()
}

cond 始终为假,因此永远不会调用doSomething

我想做这样的事情:

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

现在对我们(希望对编译器而言)很明显,这段代码没有被使用。

编译器会删除所有这些if 条件还是会离开它并且永远不会进入?

P.S.:我不能为此目的使用 #if 0

【问题讨论】:

  • cond 将在运行时进行评估 - 您希望如何在编译时知道它?
  • @al-Acme 我猜 OP 是在询问 REMOVE_UNUSED_CODE
  • 我不确定编译器标准,但编译器应该能够识别REMOVE_UNUSED_CODE 并排除死部分,并启用适当的优化。不过不太确定。
  • 你不能试试吗?如果您甚至不告诉使用您要询问的编译器(以及哪个优化级别),也没有人可以告诉您“该”编译器会做什么。我非常有信心大多数启用优化的编译器都能够找出x && false == false - 假设cond 可以很容易地确定没有副作用。
  • 这显然是针对安全关键系统(如下所述)的事实有点重要,并且应该成为问题 - 通常不允许在相同的情况下优化安全/可靠的代码作为“常规”代码的方式,因为高端编译器优化有时会引入与最初编写的逻辑不同的逻辑(我认为它不会改变对这个特定问题的实际答案 - 这是一个非常基本优化,第一层 - 但一般来说,这是一件非常重要的事情。

标签: c compilation


【解决方案1】:

GCC 将显式删除具有控制它们的常量表达式和the GNU coding standards explicitly recommend that you take advantage of this 的条件块,而不是使用诸如依赖预处理器之类的粗糙方法。尽管使用预处理器#if 可能看起来更有效,因为它会在早期阶段删除代码,但在实践中,当您向现代优化器提供尽可能多的信息时(当然,如果您可以避免,它会使您的程序更清晰和更一致)在不需要的地方添加相分离依赖项)。

这样的决定对于现代编译器来说非常、非常容易 - 即使是非常简单的 TCC 也会执行一些死代码消除; LLVM 和 GCC 等强大的系统会发现这一点,以及您作为人类读者可能会错过的更复杂的情况(通过跟踪变量的生命周期和修改,而不仅仅是查看单个点)。

这是对完整编译的其他优点的补充,例如,if 中的代码将被检查错误(而#if 对于删除可能的代码更有用> 在您当前的系统上出现错误,例如对不存在的平台函数的引用)。

【讨论】:

    【解决方案2】:

    试试

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

    相反。 别忘了做一个

    #define REMOVE_UNUSED_CODE
    

    某处。

    【讨论】:

      【解决方案3】:

      答案取决于编译器的实现。你永远不应该依赖这个。

      改为明确:

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

      【讨论】:

      • 我看不出它如何依赖于编译器。它必须是一个非常愚蠢的编译器,而不是优化未使用的代码。我认为市场上不存在这样糟糕的编译器。
      • 我同意,我会假设相同。但是要绝对确定,明确表示更安全。如果结果是生或死,你就不会做出这样的假设。
      • 如果结果将是生死攸关(安全关键型应用程序),那么此类系统有许多标准明确禁止此类结构。在安全关键系统中,不允许有任何从未执行过的代码。
      • 这里就是这种情况..这是一个安全关键系统,我正在进行覆盖测试以删除所有未执行的代码。第一步是否禁用所有这些代码,然后重新运行主要测试以查看没有发现性能问题
      • 如果您想确定,只需查看目标代码即可。使用 c 代码对程序集进行组合的开关。
      【解决方案4】:

      查看您的代码的 sn-p

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

      REMOVE_UNUSED_CODE 在编译前被预处理器替换为0。 所以if(cond && 0) 将始终被评估为假并且永远不会被执行。所以你可以说,不管cond 是什么,它永远都是假的。

      所以我宁愿这样做:

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

      【讨论】:

        【解决方案5】:

        你不应该那样做:

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

        然后用

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

        body 没有被评估,但f 应该被评估。

        如果是

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

        由于&&operator 的快捷规则,不评估f。

        如果你使用

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

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

        【讨论】:

        • 快捷方式评估仅在 c++ 中,而不是 c
        • @PhilWilliams、this 和维基百科(引用标准)另有说明。
        • 对不起。你是对的。不知道我从哪里得到这样的想法?
        【解决方案6】:

        不是直接的答案,但可能会有所帮助。有特定于编译器的内部函数来执行代码删除。

        对于 MSVC,它是 __assume(0)

        对于 GCC/Clang,它是 __builtin_unreachable()

        但是,请注意,如果您编写如下内容:

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

        您的条件永远不应评估为true,否则行为未定义。 将&& REMOVE_UNUSED_CODE__builtin_unreachable() 结合使用可确保您的代码将被编译器删除。

        【讨论】:

          猜你喜欢
          • 2011-01-03
          • 2016-03-08
          • 1970-01-01
          • 2017-09-12
          • 2018-05-10
          • 1970-01-01
          • 2017-10-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多