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

lztngnrs  于 2023-01-20  发布在  其他
关注(0)|答案(3)|浏览(137)

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

#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;
}

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

#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;
}

这些代码究竟有何不同?
为甚么会这样呢?我实在不明白上述两个密码会给予甚么分别。

olmpazwi

olmpazwi1#

问题出在PI宏上:

#define PI 22/7

这样做有两个问题:(1)它没有被括号括起来,以防止它的一部分与表达式的其他部分绑定得更紧,(2)它是在做整数除法。
如果22/7本身就是3,这可能不是您想要的,但是AREA宏也有问题,它看起来像这样:

#define AREA(r)  r * r * PI

扩展PI后,这相当于:

#define AREA(r)  r * r * 22/7

假设r是一个简单的变量或常量,则等价于:

#define AREA(r)  (r * r * 22) / 7

如果r是一个整数,它仍然在做整数除法,但它只在末尾截断,所以结果会更精确。
请注意,AREA自身存在两个问题:(1)它没有将参数r括在括号中,(2)它没有将表达式作为一个整体括在括号中。
对于问题(1),假设将2+3作为r传递,由于乘法比加法关联更紧密,这显然不会达到您的目的。
如果你想得到精确的结果,你通常会用精确的浮点值来表示pi,而不是用极不精确的近似值22/7。但是如果你确实想用22/7,你还是应该用浮点值来表示:

#define PI (22.0/7.0)

然后可以修复AREA

#define AREA(r)  (PI * (r) * (r))

这将适当地保护参数和最终结果,防止它们在展开后重新关联,但注意结果将具有类型double,还要注意其参数r被求值两次。
但是更好的pi近似值是可取的,在Linux上,math.h导出M_PI,这要精确得多。

x8goxv8g

x8goxv8g2#

试着展开宏,第一个宏是22 / 7 * 8 * 8,所有的值都是整数,所以会有舍入误差,计算结果基本上是3 * 8 * 8 = 192。
第二个表达式为8 * 8 * 22 / 7。因此计算结果为1408 / 7 = 201。

l7wslrjt

l7wslrjt3#

乘法和除法是从左到右进行的。因此,第一个示例中的AREA(8)扩展为22/7 * 8 * 8,其计算如下:((((22/7)* 8)* 8),即3 * 8 * 8 = 192。
另一个变量给出:((((八 * 八)* 二十二)/七)=(六十四 * 二十二)/七=一千四百零八/七=二百零一。
如果你想要更好的精度,使用浮点数,例如#define PI(22.0/7.0)或者,为什么不使用一个更好的近似值:#定义PI(3.141593)
但是你应该在printf中使用%f。(如Jabberwocky所评论的)

相关问题