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

A_U*_*ser 6 c optimization

考虑以下假设类型:

typedef struct Stack {
    unsigned long len;
    void **elements;
} Stack;
Run Code Online (Sandbox Code Playgroud)

而下面的假设宏与类型处理(纯粹以增强可读性).在这些宏我假设给定的参数的类型为(堆叠*)而不是仅仅堆栈(我也懒得打字了_Generic表达这里.)

#define stackNull(stack) (!stack->len)
#define stackHasItems(stack) (stack->len)
Run Code Online (Sandbox Code Playgroud)

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

int thingy = !!31337;
printf("%d\n", thingy);
if (thingy)
    doSomethingImportant(thingy);
Run Code Online (Sandbox Code Playgroud)

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

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

int thingy = wellOkaySoImNotAConstantThingyAnyMore();
if (!!thingy)
    doSomethingFarLessImportant();
Run Code Online (Sandbox Code Playgroud)

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

if (thingy)
    doSomethingFarLessImportant();
Run Code Online (Sandbox Code Playgroud)

如果是这样,它会扩展到(!!!!!thingy)等等吗?(然而,这是一个稍微不同的问题,因为这可以在任何情况下进行优化,!thingy!!!!!thingy无论什么时候,就像-(-(-(1))) = -1.)

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

Sha*_*our 7

这似乎是一个非常合理的优化和使用带有此代码的godbolt的快速测试(请参见实时):

#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 ) ;
}
Run Code Online (Sandbox Code Playgroud)

似乎表明gcc做得很好,它会产生以下结果:

func:
    testl   %edi, %edi  # x
    jne .L4 #,
    movl    $.LC1, %edi #,
    jmp puts    #
.L4:
    movl    $.LC0, %edi #,
    jmp puts    #
Run Code Online (Sandbox Code Playgroud)

我们可以从第一行看到:

testl   %edi, %edi  # x
Run Code Online (Sandbox Code Playgroud)

它只是在x不对其进行任何操作的情况下使用,还注意到优化器足够聪明,可以将两个测试组合成一个,因为如果第一个条件是true另一个必须false.

注意我使用printfscanf副作用来阻止优化器优化所有代码.

  • 实际上,即使使用`-O0`(优化关闭),我安装的gcc-4.8.3副本也会生成`!x`和`!!! x`的相同代码. (2认同)