我正在通过我的教授的测验答案,一个问题是:
像绝对值的宏这样的函数的正确实现是:
#define abs(x) ((x)<0 ? (-x) : (x))
#define abs(x) ((x)<0 ? -(x) : (x))
Run Code Online (Sandbox Code Playgroud)
为什么第二个与第一个相比是正确的?
为什么你必须使用所有().喜欢涉及的规则是什么?每个变量都需要一个()?谢谢.
mar*_*ets 30
额外括号解决了各种相关问题.我将逐一介绍它们:
int y = abs( a ) + 2
我们假设您使用:
#define abs(x) (x<0)?-x:x
...
int y = abs( a ) + 2
Run Code Online (Sandbox Code Playgroud)
这扩展到int y = (a<0)?-a:a+2
.在+2
仅绑定到错误的结果.2仅在a为正时添加,而在为负时添加.所以我们需要围绕整个事项的括号:
#define abs(x) ( (x<0) ? -x : x )
Run Code Online (Sandbox Code Playgroud)
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 )
Run Code Online (Sandbox Code Playgroud)
int y = abs(a=b);
这应该是合法的(尽管很糟糕),但它扩展到int y = ( (a=b<0)?-(a=b):a=b );
试图将最终的b分配给三元组.这不应该编译.(注意它在C++中.我必须使用gcc而不是g ++编译它,看它无法使用"赋值中的无效左值"错误进行编译.)
#define abs(x) ( (x<0) ? -(x) : (x) )
Run Code Online (Sandbox Code Playgroud)
int y = abs((a<b)?a:b);
这扩展为int y = ( ((a<b)?a:b<0) ? -((a<b)?a:b) : (a<b)?a:b )
,<0
将b与b组合,而不是按预期组合整个三元组.
#define abs(x) ( ( (x) < 0) ? -(x) : (x) )
Run Code Online (Sandbox Code Playgroud)
最后,每个实例x
都容易出现一些需要解决括号的分组问题.
所有这些中的共同线程是运算符优先级:如果您在abs(...)
调用中放置一个优先级较低的操作符x
,那么宏中使用的位置就会出现问题,那么它将绑定不正确.例如,abs(a=b)
将扩展到a=b<0
与a=(b<0)
... 相同的......不是调用者的意思.
abs
当然,这是实现abs的错误方法...如果你不想使用内置函数(你应该,因为它们将针对你移植到的任何硬件进行优化),那么它应该是一个内联模板(如果使用C++)的原因与Meyers,Sutter等人讨论重新实现最小和最大函数时提到的相同.(其他答案也提到过:会发生什么abs(x++)
?)
在我的头脑中,合理的实施可能是:
template<typename T> inline const T abs(T const & x)
{
return ( x<0 ) ? -x : x;
}
Run Code Online (Sandbox Code Playgroud)
这里可以省略括号,因为我们知道x是单个值,而不是宏的任意扩展.
更好的是,正如Chris Lutz在下面的评论中指出的那样,您可以使用模板专业化来调用优化版本(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))
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
Dan*_*son 14
是的,每个变量都需要直接围绕它.
原因是因为你可以将事物传递给不是"好"的宏,比如算术表达式或任何不是单个变量的表达式.应该很容易看出,abs(1+2)
扩展-(1 + 2)
将产生不同的结果(-1 + 2)
.这就是为什么-(x)
更正确的原因.
不幸的是,宏都不安全,你应该使用内联函数来代替这样的东西.考虑:
abs (x++); // expands to ((x++) < 0 ? - (x++) : (x++))
Run Code Online (Sandbox Code Playgroud)
这显然是宏的错误,但如果使用内联函数,它将正常工作.
使用宏而不是函数也存在其他问题.看到这个问题.
编辑:注意问题是关于C,内联函数可能只在C99中可用.