#define SWAP(a,b) { a^=b ; b^=a ; a^=b; }
if (x < 0)
SWAP(x,y);
else
SWAP(y,x);
上面给出的代码不起作用。编译时会出现以下错误。
trial.c:3:1: error: expected identifier or '(' before 'if'
trial.c:5:1: error: expected identifier or '(' before 'else'
我试图找出它无法正常工作的确切原因。预处理器按如下方式扩展宏
if (x < 0)
{
x^=y ;
y^=x ;
x^=y ;
};
else
{
y^=x ;
x^=y ;
y^=x ;
};
我怀疑花括号末端的分号是导致问题的原因。但我不确定。谁能解释一下?
答案 0 :(得分:5)
如果你想要一个扩展为语句的宏,那么do / while(0)技巧就是你的选择。
但是,如果可能的话,它可以更灵活地将宏扩展为表达式。如果要在语句上下文中使用它,只需添加分号:
#define SWAP(a, b) ( (a)^=(b), (b)^=(a), (a)^=(b) )
原始宏的问题,或者说使用它的方式,是添加的分号创建了一个额外的语句。 if
- else
语句的语法是
if (
表达 )
声明 else
声明
通过提供块{ ... }
和分号,您在if
和else
之间有两个语句。
do / while(0)技巧解决了分号问题。编写一个扩展为表达式的宏(这并不总是可行)可以完全避免它。
更多观察结果:
您好像期待SWAP(x, y)
和SWAP(y, x)
做不同的事情。他们的行为应该相同。
xor hack确实允许您在不使用临时的情况下交换变量,但99%的情况下使用临时更有意义。使用临时的一个问题是你必须声明它,这意味着你必须知道类型;这并不总是可行的。 xor hack的一个缺点是,如果两个操作数是同一个对象(比如,arr[i]
和arr[j]
,其中i ==j
),它就不起作用。它只适用于整数。
答案 1 :(得分:2)
我怀疑大括号末端的分号是导致问题的原因
这是完全正确的!如果你在一个带花括号的块之后加一个分号,那么这将是if
之后的一个新的空语句。编译器会像这样解析它:
if (x < 0) // The "if"
{ // The body of the "if"
x^=y ;
y^=x ;
x^=y ;
}
; // <<== An empty statement after the one-sided "if"
else // <<== A "stray" else (syntax error)
这是宏的常见陷阱:如果要在宏中使用花括号,请使用do / while(0)
技巧(解释here):
#define SWAP(a,b) do { a^=b ; b^=a ; a^=b; } while (0)
注意:我希望这是一项学习练习,因为将A
与B
交换并将B
与A
交换没有区别。
答案 2 :(得分:1)
是的,此时分号不合法。 if(condition) { ... };
不是格式正确的if
语句,可以是if(condition) { ... }
或if(condition) statement;
一个经典的技巧是将宏内容包装为do { contents } while(0)
。在这种情况下,预期在宏调用之后直接使用分号,因此感觉就像一个函数。
然后扩展到
if (x < 0)
do { a^=b ; b^=a ; a^=b; } while (0);
这是if
表达式的有效形式:if(condition) statement;