额外的括号可以解决各种相关问题。我会一一介绍:
试试:int y = abs( a ) + 2
假设您使用:
#define abs(x) (x<0)?-x:x
...
int y = abs( a ) + 2
这扩展为int y = (a<0)?-a:a+2。 +2 仅绑定到错误结果。 2 仅在 a 为正时添加,而不是在它为负时添加。所以我们需要括号括起来:
#define abs(x) ( (x<0) ? -x : x )
试试:int y = abs(a+b);
但是我们可能有int y = abs(a+b) 扩展为int y = ( (a+b<0) ? -a+b : a+b)。如果 a + b 为负数,则当它们为结果相加时 b 不会被否定。所以我们需要把-x的x放在括号里。
#define abs(x) ( (x<0) ? -(x) : x )
试试:int y = abs(a=b);
这应该是合法的(虽然不好),但它会扩展为int y = ( (a=b<0)?-(a=b):a=b );,它试图将最后的 b 分配给三元组。这不应该编译。 (请注意,它在 C++ 中执行。我必须使用 gcc 而不是 g++ 编译它才能看到它无法编译并出现“invalid lvalue in assignment”错误。)
#define abs(x) ( (x<0) ? -(x) : (x) )
试试:int y = abs((a<b)?a:b);
这扩展为int y = ( ((a<b)?a:b<0) ? -((a<b)?a:b) : (a<b)?a:b ),它将<0 与b 组合在一起,而不是预期的整个三元组。
#define abs(x) ( ( (x) < 0) ? -(x) : (x) )
最后,x 的每个实例都容易出现一些需要括号来解决的分组问题。
常见问题:运算符优先级
所有这些的共同点是operator precedence:如果您在abs(...) 调用中放置一个优先级低于宏中使用x 的运算符,那么它将错误地绑定。例如,abs(a=b) 将扩展为 a=b<0,这与 a=(b<0) 相同......这不是调用者的意思。
实施abs的“正确方法”
当然,无论如何,这是实现 abs 的错误方法...如果您不想使用内置函数(并且应该,因为它们将针对您移植到的任何硬件进行优化),那么它应该是内联模板(如果使用 C++),原因与 Meyers、Sutter 等人讨论重新实现 min 和 max 函数时提到的相同。 (其他回答也提到过:abs(x++)会怎样?)
在我的脑海中,一个合理的实现可能是:
template<typename T> inline const T abs(T const & x)
{
return ( x<0 ) ? -x : x;
}
这里可以省略括号,因为我们知道 x 是单个值,而不是宏的任意扩展。
更好的是,正如 Chris Lutz 在下面的 cmets 中指出的那样,您可以使用模板专业化来调用优化版本(abs、fabs、labs)并获得类型安全、支持非内置类型以及性能。
测试代码
#if 0
gcc $0 -g -ansi -std=c99 -o exe && ./exe
exit
#endif
#include <stdio.h>
#define abs1(x) (x<0)?-x:x
#define abs2(x) ((x<0)?-x:x)
#define abs3(x) ((x<0)?-(x):x)
#define abs4(x) ((x<0)?-(x):(x))
#define abs5(x) (((x)<0)?-(x):(x))
#define test(x) printf("//%30s=%d\n", #x, x);
#define testt(t,x) printf("//%15s%15s=%d\n", t, #x, x);
int main()
{
test(abs1( 1)+2)
test(abs1(-1)+2)
// abs1( 1)+2=3
// abs1(-1)+2=1
test(abs2( 1+2))
test(abs2(-1-2))
// abs2( 1+2)=3
// abs2(-1-2)=-1
int a,b;
//b = 1; testt("b= 1; ", abs3(a=b))
//b = -1; testt("b=-1; ", abs3(a=b))
// When compiled with -ansi -std=c99 options, this gives the errors:
//./so1a.c: In function 'main':
//./so1a.c:34: error: invalid lvalue in assignment
//./so1a.c:35: error: invalid lvalue in assignment
// Abs of the smaller of a and b. Should be one or two.
a=1; b=2; testt("a=1; b=2; ", abs4((a<b)?a:b))
a=2; b=1; testt("a=2; b=1; ", abs4((a<b)?a:b))
// abs4((a<b)?a:b)=-1
// abs4((a<b)?a:b)=1
test(abs5( 1)+2)
test(abs5(-1)+2)
test(abs5( 1+2))
test(abs5(-1-2))
b = 1; testt("b= 1; ", abs5(a=b))
b = -1; testt("b=-1; ", abs5(a=b))
a=1; b=2; testt("a=1; b=2; ", abs5((a<b)?a:b))
a=2; b=1; testt("a=2; b=1; ", abs5((a<b)?a:b))
}
输出
abs1( 1)+2=3
abs1(-1)+2=1
abs2( 1+2)=3
abs2(-1-2)=-1
a=1; b=2; abs4((a<b)?a:b)=-1
a=2; b=1; abs4((a<b)?a:b)=1
abs5( 1)+2=3
abs5(-1)+2=3
abs5( 1+2)=3
abs5(-1-2)=3
b= 1; abs5(a=b)=1
b=-1; abs5(a=b)=1
a=1; b=2; abs5((a<b)?a:b)=1
a=2; b=1; abs5((a<b)?a:b)=1