为什么即使条件为假,这个If语句也会执行?

时间:2014-01-10 14:15:08

标签: c macros

#include<stdio.h>

int main()    
{    
    // We use a trick involving exclusive-or to swap two variables
    #define SWAP(a, b)  a ^= b; b ^= a; a ^= b; 

    int x = 10;
    int y = 5;
    printf("%d%d\n",x,y );

    if(x < 0)
        SWAP(x, y);

    printf("%d%d",x,y );

    return 0;     
}

输出结果为:

105
515

即使条件在if(10 <0为假)中评估为假,这个5 15即将来临?

4 个答案:

答案 0 :(得分:9)

您的SWAP宏调用将被预处理器替换。虽然它出现在一行上,但只有第一个语句是if语句的一部分。

if(x < 0)
    SWAP(x, y);

由预处理器转换为:

if(x < 0)
    a ^= b; b ^= a; a ^= b; ;

实际上与以下内容相同:

if(x < 0)
    a ^= b; 
b ^= a; 
a ^= b;
; 

您可以通过将宏定义中的所有语句放入块中来解决此问题,如下所示:

#define SWAP(a, b)  do { a ^= b; b ^= a; a ^= b; } while( 0 )

do / while技巧允许您使用分号跟随SWAP调用。

答案 1 :(得分:2)

因为宏体不是声明:它是三个。

如果您将宏替换为其定义,if读取如下:

if(x < 0)
  x ^= y; x ^= y; x ^= y;

当然与以下内容相同:

if(x < 0)
  x ^= y;
x ^= y;
x ^= y;

由于宏调用周围没有大括号,因此只有第一个分配“属于”if

修复是将宏包含在“虚拟”循环中:

#define SWAP(a, b) do { a ^= b; b ^= a; a ^= b; } while(0)

请注意,宏定义以分号结尾。

答案 2 :(得分:1)

#define语句表现为由预处理器完成的文本的宏替换。您的if语句实际上已转换为此语句(在编译之前):

if(x < 0)
    x ^= y; x ^= y; x ^= y;;

注意你没有任何花括号,if里面有多个表达式。只有第一个没有被执行。

每个编译器都应该支持一个标志来只输出预处理的代码。如果是gcc,你可以看到它:gcc -E -P i.c如果你有疑问。

答案 3 :(得分:0)

您的SWAP宏已扩展为三个语句:其中只有一个适用于if语句。您可以通过在宏中添加大括号来修复它,或者更好的是,使用虚拟loop结构。

但请注意;如果你真的想要使用XOR交换,那么你需要检查对象的地址是不一样的。否则你会得到好奇的归零效果。

总而言之,你得到了这个

#define SWAP(a, b)  do { if (&a != &b){ a ^= b; b ^= a; a ^= b; } } while 0

解决了这两个问题。 do while循环意味着SWAP的用户必须以;终止:在构建宏时,这是一种很好的做法。