C语言中的预处理器是如何工作的?

7
为什么下面代码的答案是16?有人能解释一下这个程序的运作方式吗?
#define SQUARE(n) n*n
void main()
{
    int j;      
    j =16/SQUARE(2);

    printf("\n j=%d",j);
    getch();
}

如果我们写的代码与下面相同,则答案是4:
//the ans is 4 why?
#include<stdio.h>
#include<conio.h>

#define SQUARE(n) n*n

void main()
{
    int j;      
    j =16/(SQUARE(2));

    printf("\n j=%d",j);
    getch();
}

1
这就是为什么你应该避免类似函数的宏定义的许多原因之一。 - Lundin
1
加括号还是不加括号,这是个问题。答案是:在编写表达式时始终使用括号。 - Marius Bancila
是的,对于初学者,尤其会发生许多意想不到的事情。 - Arvind Lairenjam
1
请注意,如果您写成了 j = 16 / SQUARE(1 + 1);,那么答案将是18,但如果您写成了 j = 16 / (SQUARE(1 + 1));,那么答案将是5。此外,main()函数的返回类型应为int - Jonathan Leffler
9个回答

16

预处理器会严格按照文字内容进行替换。

因此,宏调用SQUARE(2)就会被替换成2*2

在你的例子中,这意味着整个表达式变成了16/2*2,由于 C 的优先级规则,它会被计算为(16/2)*2,即16。

宏应该始终使用括号括起来,并且每个参数也要括起来。

如果我们这样做,就会得到:

#define SQUARE(n)  ((n) * (n))

这将替换为16/((2) * (2)),它计算为16/4,即4。

每个参数周围的括号使得像SQUARE(1+1)这样的调用按预期工作,如果没有它们,如16/SQUARE(1+1)的调用将变成16/(1+1*1+1),即16/3,完全不是你想要的。


非常好的解释。非常感谢您,unwind。 - Arvind Lairenjam

3
您需要使用隔离括号定义宏,示例如下:

您需要使用隔离括号定义宏,示例如下:

 #define SQUARE(n) ((n)*(n))

否则
 j = 16/SQUARE(2);

扩展为

j = 16 / 2 * 2;   which is equivalent to (16 / 2) * 2

当您想要的是

j = 16 / (2 * 2);   

3

运算顺序。您的表达式正在计算为:

 j = 16 / 2 * 2

这个等于16。把它变成:

#define SQUARE(n) (n*n) 

这将强制先计算正方形。


现在,我知道答案了。谢谢。 - Arvind Lairenjam

3

1. 当使用宏作为表达式时,应将整个宏主体括在括号中。

这可以防止错误扩展,例如:

#define SQUARE(x) x*x
-SQUARE(5,5)
// becomes -5 * 5

2. 如果宏参数是表达式,你也应该把它们括起来。

这样可以防止另一种类型的问题:

#define SQUARE(x) x*x
SQUARE(5+2)
// becomes 5 + 2*5 + 2

因此,正确的写法是这样的:
#define square(n) ((n)*(n))
-SQUARE(5+2)
// becomes -((5+2)*(5+2))

虽然可以将宏用作函数(猜猜为什么),但不建议这样做,最好使用函数。例如:

inline double square(n) { return n*n; }

谢谢您,Kos。感谢您的解释。 - Arvind Lairenjam

2
宏的扩展将会像这样:
  j = 16/SQUARE(2);
  j = 16/2*2;

等同于:j = (16/2)*2; 意思是 j = 16;

另外:

 j = 16/(SQUARE(2));
 j = 16/(2*2);

这等同于:j = 16/4; 意思是 j = 4;


谢谢Midhun,我现在明白了。 - Arvind Lairenjam

2
由于宏将被展开为:
j = 16/2*2;

预编译器不会对扩展进行任何处理。它将扩展后的宏原封不动地放在您的代码中。由于您没有给替换文本加括号,因此它也不会在主代码中为您加上。请改为:

#define SQUARE(n) ((n)*(n))

谢谢Deepankar,现在我知道了。 - Arvind Lairenjam
1
虽然你的替代定义更好,但在使用SQUARE(1+1)时会遇到问题,因为你的代码会返回3。请给每个参数和整个表达式加上括号:#define SQUARE(n) ((n) * (n)) - Jonathan Leffler

1

第一个示例的评估结果为:

16 / 2 * 2
(16 / 2) * 2
8 * 2
16

第二个例子的评估结果是:

16 / (2 * 2)
16 / 4
4

在预处理语句中添加括号以控制操作顺序:
#define SQUARE(n) ((n)*(n))

((n)*(n))中的外括号确保在执行任何外部操作之前,n被平方。内部括号(n)确保在像这样向SQUARE传递表达式的情况下正确评估n:
16 / SQUARE(2 * 2)
16 / ((2 * 2)*(2 * 2))
16 / (4 * 4)
16 / 16
1

是的 Nocturno,我现在明白了。非常感谢你。 - Arvind Lairenjam

0
Its because Whenever macro name is used, it is replaced by the contents of the macro.its simple rule of working of macro.


Case 1 : result 16

        define SQUARE(n) n*n
        void main()
         {
            int j;      
            j =16/SQUARE(2);

          printf("\n j=%d",j);
          getch();
       }

its get expand as below


j =16/SQUARE(2); 

so in place of SQUARE(2) it will replace 2*2 because Macro is SQUARE(n) n*n

j = 16/2*2
j = (16/2)*2
j = 8*2
j =16



Case 2 : result 4



        define SQUARE(n) n*n

        void main()
       {
            int j;      
            j =16/(SQUARE(2));

          printf("\n j=%d",j);
          getch();
       }



its get expand as below


j =16/(SQUARE(2));

so in place of SQUARE(2) it will replace 2*2 because Macro is SQUARE(n) n*n

j = 16/(2*2)
j = 16/(4)
j = 4


Hope this will help

0

你会得到

j =16/2*2; // (16 / 2) * 2 = 16

1
我认为16/2*2与(16/2)*2相同,因此结果是16。谢谢Johann。 - Arvind Lairenjam
*duh* 数学很难...谢谢! - Johann Gerell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接