C语言 C 宏问题 -(x) 与 (-x)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2025372/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C Macro Question -(x) vs (-x)
提问by Crystal
I'm going through quiz answers from my professor and a question was:
我正在查看教授的测验答案,一个问题是:
the correct implementation of a function like macro for absolute value is:
像宏这样的绝对值函数的正确实现是:
#define abs(x) ((x)<0 ? (-x) : (x))
#define abs(x) ((x)<0 ? -(x) : (x))
Why is the second one correct vs the first one?
为什么第二个比第一个正确?
And why do you have to use all the (). Like what are the rules involved? Every variable needs a ()? Thanks.
以及为什么你必须使用所有的 ()。比如涉及到什么规则?每个变量都需要一个 ()? 谢谢。
回答by markets
There are various related problems that the extra parentheses solve. I'll go through them one by one:
额外的括号可以解决各种相关问题。我将一一介绍:
Try: int y = abs( a ) + 2
尝试: int y = abs( a ) + 2
Let's assume you use:
假设您使用:
#define abs(x) (x<0)?-x:x
...
int y = abs( a ) + 2
This expands to int y = (a<0)?-a:a+2. The +2binds only to the false result. 2 is only added when a is positive, not when it is negative. So we need parenthesis around the whole thing:
这扩展到int y = (a<0)?-a:a+2. 在+2仅绑定到错误的结果。2 只在 a 为正时加,当 a 为负时不加。所以我们需要在整个事情周围加上括号:
#define abs(x) ( (x<0) ? -x : x )
Try: int y = abs(a+b);
尝试: int y = abs(a+b);
But then we might have int y = abs(a+b)which gets expanded to int y = ( (a+b<0) ? -a+b : a+b). If a + b is negative then b is not negated when they add for the result. So we need to put the xof -xin parentheses.
但随后我们可能会将int y = abs(a+b)which 扩展为int y = ( (a+b<0) ? -a+b : a+b). 如果 a + b 是负数,那么当他们为结果相加时 b 不会被否定。所以我们需要把xof-x放在括号里。
#define abs(x) ( (x<0) ? -(x) : x )
Try: int y = abs(a=b);
尝试: int y = abs(a=b);
This ought to be legal (though bad), but it expands to int y = ( (a=b<0)?-(a=b):a=b );which tries to assign the final b to the ternary. This should not compile. (Note that it does in C++. I had to compile it with gcc instead of g++ to see it fail to compile with the "invalid lvalue in assignment" error.)
这应该是合法的(虽然不好),但它扩展到int y = ( (a=b<0)?-(a=b):a=b );试图将最后的 b 分配给三元。这不应该编译。(请注意,它在 C++ 中确实如此。我必须使用 gcc 而不是 g++ 编译它才能看到它无法编译并出现“无效的左值赋值”错误。)
#define abs(x) ( (x<0) ? -(x) : (x) )
Try: int y = abs((a<b)?a:b);
尝试: int y = abs((a<b)?a:b);
This expands to int y = ( ((a<b)?a:b<0) ? -((a<b)?a:b) : (a<b)?a:b ), which groups the <0with the b, not the entire ternary as intended.
这将扩展为int y = ( ((a<b)?a:b<0) ? -((a<b)?a:b) : (a<b)?a:b ),将<0与 b分组,而不是按预期将整个三元组。
#define abs(x) ( ( (x) < 0) ? -(x) : (x) )
In the end, each instance of xis prone to some grouping problem that parentheses are needed to solve.
最后,每个实例x都容易出现一些需要括号来解决的分组问题。
Common problem: operator precedence
常见问题:运算符优先级
The common thread in all of these is operator precedence: if you put an operator in your abs(...)invocation that has lower precedence then something around where xis used in the macro, then it will bind incorrectly. For instance, abs(a=b)will expand to a=b<0which is the same as a=(b<0)... that isn't what the caller meant.
所有这些中的共同点是运算符优先级:如果您在abs(...)调用中放置一个优先级较低的运算符,然后x在宏中使用的位置周围的东西,那么它将错误地绑定。例如,abs(a=b)will expand to a=b<0which is the same as a=(b<0)... 这不是调用者的意思。
The "Right Way" to Implement abs
实施的“正确方法” abs
Of course, this is the wrong way to implement abs anyways... if you don't want to use the built in functions (and you should, because they will be optimized for whatever hardware you port to), then it should be an inline template (if using C++) for the same reasons mentioned when Meyers, Sutter, et al discuss re-implementing the min and max functions. (Other answers have also mentioned it: what happens with abs(x++)?)
当然,无论如何这是实现 abs 的错误方法......如果你不想使用内置函数(你应该,因为它们会针对你移植到的任何硬件进行优化),那么它应该是一个内联模板(如果使用 C++),原因与 Meyers、Sutter等人讨论重新实现 min 和 max 函数时提到的原因相同。(其他答案也提到了:会发生什么abs(x++)?)
Off the top of my head, a reasonable implementation might be:
在我的脑海里,一个合理的实现可能是:
template<typename T> inline const T abs(T const & x)
{
return ( x<0 ) ? -x : x;
}
Here it is okay to leave off the parentheses since we know that x is a single value, not some arbitrary expansion from a macro.
这里可以省略括号,因为我们知道 x 是单个值,而不是宏的任意扩展。
Better yet, as Chris Lutz pointed out in the comments below, you can use template specialization to call the optimized versions (abs, fabs, labs) and get all the benefits of type safety, support for non-builtin types, and performance.
更好的是,正如 Chris Lutz 在下面的评论中指出的那样,您可以使用模板专业化来调用优化版本(abs、fabs、labs)并获得类型安全、支持非内置类型和性能的所有好处。
Test Code
测试代码
#if 0
gcc 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
-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))
}
Output
输出
abs (x++); // expands to ((x++) < 0 ? - (x++) : (x++))
回答by Dan Olson
Yes, every variable needs parenthesis around it directly.
是的,每个变量都需要直接用括号括起来。
The reason is because you can pass things into the macro that aren't "nice", like arithmetic expressions or really any expression that isn't a single variable. It should be easy to see that with abs(1+2), the expanded -(1 + 2)will give a different result than (-1 + 2). This is why -(x)is more correct.
原因是因为您可以将不“好”的东西传递给宏,例如算术表达式或任何不是单个变量的表达式。应该很容易看出,abs(1+2)扩展-(1 + 2)将给出与 不同的结果(-1 + 2)。这就是为什么-(x)更正确。
Unfortunately neither macro is safe, you should use an inline function for things like this instead. Consider:
不幸的是,这两个宏都不安全,您应该使用内联函数来代替这样的事情。考虑:
##代码##This is clearly wrong with the macro, but it would work correctly if an inline function was used instead.
宏显然是错误的,但如果使用内联函数,它会正常工作。
There are other problems with using macros instead of functions as well. See this question.
使用宏代替函数还有其他问题。看到这个问题。
Edit: Noting that the question is about C, inline functions may only be available in C99.
编辑:注意这个问题是关于 C 的,内联函数可能只在 C99 中可用。

