下面的 C 宏示例如何编译?

0 c embedded c-preprocessor

下面的代码没有给出正确的输出。

#include <stdio.h>
#define PI 22/7
#define AREA(r)  PI * r * r
int main() {
    printf("Pi: %d\n", PI);
    printf("Area: %d\n", AREA(8));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

而下面的代码给出了正确的(最接近的)输出。

#include <stdio.h>
#define PI 22/7
#define AREA(r)  r * r * PI
int main() {
    printf("Pi: %d\n", PI);
    printf("Area: %d\n", AREA(8));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这些代码究竟有何不同?

为什么会这样呢?我无法理解上述代码给出不同答案的区别。

Tom*_*zes 7

问题是你的PI宏:

#define PI 22/7
Run Code Online (Sandbox Code Playgroud)

这样做有两个问题:(1)它没有用括号括起来,以防止它的某些部分与它所扩展的表达式的其他部分更紧密地结合,以及(2)它正在进行整数除法。

如果你22/7自己有的话,那就是3。想必这不是您想要的。但是这个AREA宏也有问题,如下所示:

#define AREA(r)  r * r * PI
Run Code Online (Sandbox Code Playgroud)

扩展后PI,这相当于:

#define AREA(r)  r * r * 22/7
Run Code Online (Sandbox Code Playgroud)

假设r是一个简单的变量或常量,这相当于:

#define AREA(r)  (r * r * 22) / 7
Run Code Online (Sandbox Code Playgroud)

如果r是整数,仍然是整数除法,只是最后截断,所以结果会更准确。

请注意,AREA它自己有两个问题:(1) 它没有将其参数 括r在括号中,(2) 它没有将整个表达式括在括号中。

对于问题 (1),想象一下传递类似2+3as的东西r。由于乘法比加法关联得更紧密,因此这显然不会达到您想要的效果。

如果您想要准确的结果,通常会使用精确的浮点值 pi,而不是极其不准确的近似值 22/7。但如果你确实想使用 22/7,你仍然应该使用浮点值:

#define PI (22.0/7.0)
Run Code Online (Sandbox Code Playgroud)

然后你可以修复AREA

#define AREA(r)  (PI * (r) * (r))
Run Code Online (Sandbox Code Playgroud)

This will properly protect the arguments and final results from reassociating after expansion, but note that the result will have type double. Also note that its argument, r, is evaluated twice.

But a better approximation to pi is preferable. On Linux, math.h exports M_PI, which is far more accurate.