今天晚上我正在阅读一些用C编写的代码,文件顶部是类似函数的宏HASH:
#define HASH(fp) (((unsigned long)fp)%NHASH)
这让我想知道,为什么有人会选择使用类似函数的宏来实现这种函数,而不是将它作为常规的vanilla C函数实现?每种实施的优缺点是什么?
谢谢你!
这样的宏可以避免函数调用的开销.
看起来似乎并不多.但在您的示例中,宏将变为1-2个机器语言指令,具体取决于您的CPU:
获取fp的内存值并将其放入寄存器中
取寄存器中的值,按固定值进行模数(%)计算,并将其保留在同一寄存器中
而功能相当的将是更多的机器语言指令,通常是类似的
将fp的值粘贴在堆栈上
调用该函数,该函数还将下一个(返回)地址放在堆栈上
也许在函数内部构建一个堆栈框架,具体取决于CPU架构和ABI约定
从堆栈中获取fp的值并将其放入寄存器中
取寄存器中的值,按固定值进行模数(%)计算,并将其保留在同一寄存器中
也许从寄存器中获取值并将其放回堆栈,具体取决于CPU和ABI
如果构建了堆栈框架,请将其展开
将返回地址弹出堆栈并继续执行指令
还有更多代码,嗯?如果你正在做一些事情,比如在GUI中的窗口中渲染成千上万个像素中的每个像素,那么如果使用宏,事情运行得更快.
就个人而言,我更喜欢使用C++内联作为更具可读性和更不容易出错的内容,但内联也更多地是对编译器的暗示,它不必采取.预处理器宏是编译器无法争辩的大锤.
基于宏的实现的一个重要优点是它不依赖于任何具体的参数类型.C语言中的函数式宏在很多方面都是C++中的模板函数(C++中的模板诞生为"更文明"的宏,BTW).在这种特殊情况下,宏的参数没有具体类型.它可能绝对是任何可转换为类型的东西unsigned long
.例如,如果用户如此满意(并且如果他们愿意接受实现定义的结果),他们可以将指针类型传递给此宏.
无论如何,我不得不承认这个宏不是宏的类型独立灵活性的最好例子,但总的来说,灵活性经常被派上用场.同样,当某个功能由某个功能实现时,它仅限于特定的参数类型.在许多情况下,为了将类似的操作应用于不同的类型,有必要提供具有不同类型的参数的几个函数(和不同的名称,因为这是C),而同样可以通过仅一个类似函数的宏来完成.例如,宏
#define ABS(x) ((x) >= 0 ? (x) : -(x))
与所有算术类型的作品,而基于函数的实现必须提供相当多的人(我暗示标准abs
,labs
,llabs
和fabs
).(是的,我知道传统上提到的这种宏的危险.)
宏并不完美,但关于"由于内联函数而不再需要类似函数的宏"的流行格言只是简单的无稽之谈.为了完全替换类似函数的宏,C将需要函数模板(如在C++中)或至少需要函数重载(如在C++中那样).如果没有那个类似函数的宏,并且仍将是C中非常有用的主流工具.