为什么预处理器宏忽略括号中的语句

时间:2018-04-09 10:06:58

标签: c c-preprocessor

关注我的{duplicatequestion(以及StoryTeller的建议)

为什么预处理器宏忽略括号中的函数名?

#include  <stdio.h>
#include <stdlib.h>
#define abs(x) ((x))

int main(void)
{
    printf("%d\n", abs(-1)); // output: -1
    printf("%d\n", (abs)(-1)); // output: 1
    return 0;
}

这是在标准中定义的吗?

2 个答案:

答案 0 :(得分:9)

预处理器的宏替换指定如下:

6.10.3 Macro replacement / p10 - 强调我的:

  

表单

的预处理指令
# define identifier lparen identifier-list<opt> ) replacement-list new-line
# define identifier lparen ... ) replacement-list new-line
# define identifier lparen identifier-list , ... ) replacement-list new-line
     

使用参数定义类似函数的宏,其用法类似   语法上的函数调用。参数由。指定   可选的标识符列表,其范围从它们的范围扩展   在标识符列表中声明,直到换行符   终止#define预处理指令。 每个后续   类似函数的宏名称的实例后跟一个(作为下一个   预处理令牌引入了预处理令牌的序列   由定义中的替换列表替换(an   调用宏)。替换的预处理序列   令牌由匹配的预处理令牌终止,跳过   插入匹配的左右括号预处理对   令牌。在预处理令牌组成的序列中   调用类似函数的宏,新行被认为是正常的   白色空间。

它以粗体显示它。要进行替换,宏名称后面的下一个预处理标记必须是(。当它是)时,例如当宏在括号中时,不会发生替换。

这样我们只能在括号中使用函数名,这个表达式与函数的指示符相同。

答案 1 :(得分:0)

为什么呢?因为 C预处理器不能使用C语言!

C预处理器被认为是纯文本替换工具,几乎不足以为C程序员提供简单的方法

  • 定义粘贴到程序中的常量

  • 将文件(即标题)的内容直接粘贴到程序

  • 提供了一种简单的方法来创建简单的代码模板,再次将其粘贴到程序中

这些都不包括对C语法的任何认识!它是纯文本操作。
相反,你可以做这样的事情

#define CLASS(name, base) typedef struct name name;\
    struct name {\
        base super;
#define END_CLASS };

CLASS(foo, bar)
    int baz;
END_CLASS

请注意,在展开{宏时,预处理器将生成不匹配的CLASS令牌,在展开}宏时,预处理器将生成无法匹配的END_CLASS令牌。

因此,使用宏的语法与调用函数的语法无关。您可以通过多种不同的方式调用C函数(foo()foo ()(foo)()(**(*foo) ) ()等),因为C语言处理函数的表达式,并定义了什么当您将其名称放在括号中时(它会隐式转换为指向它的指针),取消引用它或调用它。所有这些在预处理器中都不存在,因此只有一种方法可以使用宏:foo(),名称和(之间没有额外的空格。

边注:

C预处理器对C语言是如此不可知,它不仅仅与C一起使用,它也常用于像FORTRAN这样的语言。 (实际上,这是非常合适的。然而,C预处理器是最好的,通常支持的东西,可以用来使FORTRAN减少痛苦。)