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

Why does preprocessor macros ignore statements in parentheses

根据我的(duplicate) (以及 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;
}

标准中有定义吗?

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

6.10.3 Macro replacement / p10 - 强调我的:

A preprocessing directive of the form

# 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

defines a function-like macro with parameters, whose use is similar syntactically to a function call. The parameters are specified by the optional list of identifiers, whose scope extends from their declaration in the identifier list until the new-line character that terminates the #define preprocessing directive. Each subsequent instance of the function-like macro name followed by a ( as the next preprocessing token introduces the sequence of preprocessing tokens that is replaced by the replacement list in the definition (an invocation of the macro). The replaced sequence of preprocessing tokens is terminated by the matching ) preprocessing token, skipping intervening matched pairs of left and right parenthesis preprocessing tokens. Within the sequence of preprocessing tokens making up an invocation of a function-like macro, new-line is considered a normal white-space character.

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

所以我们只剩下括号中的函数名,一个与函数指示符相同的表达式。

为什么?因为 C 预处理器与 C 语言无关!

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

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

  • 直接将文件内容(即headers)粘贴到程序中

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

None 其中包括对 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() 名称和 (.

之间没有额外的 space
边注:

C 预处理器与 C 语言无关,它不仅与 C 一起使用,而且还经常与 FORTRAN 等语言一起使用。 (实际上,这太糟糕了。尽管如此,C 预处理器是最好的、普遍支持的东西,可以用来减轻 FORTRAN 的痛苦。)