考虑以下假设类型:
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)
保证是0
或1
。
但我想知道编译器是否会优化以下内容
int thingy = wellOkaySoImNotAConstantThingyAnyMore();
if (!!thingy)
doSomethingFarLessImportant();
这是否会被优化为在if语句中实际使用(thingy),就像if
语句被写为
if (thingy)
doSomethingFarLessImportant();
如果是,它是否会扩展为(!!!!!thingy)
,依此类推? (但这是一个稍微不同的问题,因为在任何情况下都可以优化,!thingy
无论如何都是!!!!!thingy
,就像-(-(-(1))) = -1.)
在问题标题中我说“编译器”,我的意思是我在问任何编译器是否这样做,但是我对GCC在这个实例中的行为特别感兴趣,因为它是我选择的编译器。
答案 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} }。
注意我使用false
和printf
来产生副作用,以防止优化程序优化所有代码。