为什么C预处理器宏通过简单的文本替换而不是类似函数的方式工作?

mkshixfv  于 2023-04-29  发布在  其他
关注(0)|答案(1)|浏览(194)

作为标题,我知道如果我这样做:

#include <stdio.h>
#include <stdlib.h>

#define add(a,b) a+b

int main()
{
    printf("%d\n", add(4,5)*3);
    return 0;
}

然后,由于C预处理器宏的原则,文本替换,结果是4+5*3=19而不是(4+5)*3=27。
如果我想得到(4+5)*3=27,那么代码需要修改为:

#define add(a,b) (a+b)

不过,我好奇的是:为什么C预处理器宏通过文本替换“4+5*3”工作,而不是将其视为函数“(4+5)*3”?
如果C预处理器宏以类似函数的方式工作,会出现什么问题或缺点?

dfddblmv

dfddblmv1#

C语言是在计算时代设计的,虽然它对我们中的一些人来说似乎仍然相当近和熟悉,但与今天的期望几乎无法识别。
特别是,它非常强调“小”和“简单”。PDP-11是C和Unix开始的地方,是一台16位机器,有64 KB的地址空间。(也就是说,被广泛嘲笑的MS-DOS地址空间的十分之一,以及所谓的“640 k对任何人来说都应该足够了”。因此,对于像编译器这样的复杂程序,每个功能都必须真正发挥其作用。C和Unix的设计者们把尽可能少地使用代码作为一种骄傲。
此外,C已经有了真正的工作函数(根据定义!)以适当的类似功能的方式。没有令人信服的理由给予类似函数的预处理器宏任何类型的真函数语义。预处理器的其余部分操作纯文本替换,因此在类似函数的宏参数上使用纯文本替换也是很自然的。这在很多时候都很有效,当它不起作用的时候(比如,使用额外的括号)的变通方法很明显,很容易记住,当它们不起作用的时候,那么,在这种情况下(就像今天一样),你可能应该写一个合适的函数。
C预处理器的语法和语义,虽然显然与“核心C”相当不同,但受到当时其他流行的宏预处理器的影响和相似,例如m4和内置在assemblers中的宏处理器。
对于宏参数,使用文本语义而不是“适当”语义有时也是有利的。函数必须采用可计算表达式作为参数,但预处理器宏可以采用类型名称或空运算符。例如,当我们必须转换malloc的返回值时,我总是使用

#define Salloc(type) (type *)malloc(sizeof(type))

这样我就可以说

struct foo *fp = Salloc(struct foo);

我有时也会说

#define Compare(a, op, b) (cmpfunc(a, b) op 0)

其中cmpfunc是一个返回负、0或正的函数,如果我想写

if(Compare(foo, <, goo))

另请参阅有人在软件工程上发现的this good answer。stackexchange.com网站上。
免责声明:我赶紧补充说,我不会建议使用任何SallocCompare宏今天!但在过去,我们一直在做这样的事情,这并不一定被认为是“预处理器滥用”。直到今天,标准库中还有一个宏采用了类型名称:va_arg

相关问题