编译器会优化条件中的双逻辑否定吗?

时间:2014-12-09 19:04:37

标签: c optimization

考虑以下假设类型:

typedef struct Stack {
    unsigned long len;
    void **elements;
} Stack;

以下用于处理类型的假设宏(纯粹是为了增强可读性。)在这些宏中,我 am 假设给定的参数具有类型(Stack *)而不仅仅是Stack(我可以不要在这里输入一个_Generic表达式。)

#define stackNull(stack) (!stack->len)
#define stackHasItems(stack) (stack->len)

为什么我不是简单地使用!stackNull(x)来检查堆栈是否有项目?我认为这样效率会稍微低一些(阅读:真的不明显,但我认为这很有趣),而不仅仅是检查stack->len因为它会导致双重否定。在以下情况中:

int thingy = !!31337;
printf("%d\n", thingy);
if (thingy)
    doSomethingImportant(thingy);

字符串“1 \ n”将被打印,并且不可能优化条件(实际上,如果thingy变量没有常量初始化器或者在测试之前被修改,那么这是不可能的,但是我们'在这个例子中会说31337不是常数)因为(!!x)保证是01

但我想知道编译器是否会优化以下内容

int thingy = wellOkaySoImNotAConstantThingyAnyMore();
if (!!thingy)
    doSomethingFarLessImportant();

这是否会被优化为在if语句中实际使用(thingy),就像if语句被写为

一样
if (thingy)
    doSomethingFarLessImportant();

如果是,它是否会扩展为(!!!!!thingy),依此类推? (但这是一个稍微不同的问题,因为在任何情况下都可以优化,!thingy无论如何都是!!!!!thingy,就像-(-(-(1))) = -1.)

一样

在问题标题中我说“编译器”,我的意思是我在问任何编译器是否这样做,但是我对GCC在这个实例中的行为特别感兴趣,因为它是我选择的编译器。

1 个答案:

答案 0 :(得分:7)

这看起来很漂亮reasonable optimization,并使用godbolt快速测试此代码( see it live ):

#include <stdio.h>

void func( int x)
{  
  if( !!x )
  {
    printf( "first\n" ) ;
  }

  if( !!!x )
  {
    printf( "second\n" ) ;
  }
}

int main()
{
  int x = 0 ;

  scanf( "%d", &x ) ;
  func( x ) ;
}

似乎表明gcc表现良好,它会生成以下内容:

func:
    testl   %edi, %edi  # x
    jne .L4 #,
    movl    $.LC1, %edi #,
    jmp puts    #
.L4:
    movl    $.LC0, %edi #,
    jmp puts    #

我们可以从第一行看到:

testl   %edi, %edi  # x

它只使用x而不对其执行任何操作,还注意到优化器足够聪明将两个测试合并为一个,因为如果第一个条件是true,则另一个必须是{{1} }。

注意我使用falseprintf来产生副作用,以防止优化程序优化所有代码。