考虑以下假设类型:
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)是保证是任一0或1.
但我想知道编译器是否会优化以下内容
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在这个实例中的行为,因为它是我选择的编译器.
这似乎是一个非常合理的优化和使用带有此代码的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.
注意我使用printf和scanf副作用来阻止优化器优化所有代码.