何时在C中使用类似函数的宏

Ral*_*lph 11 c function c-preprocessor

今天晚上我正在阅读一些用C编写的代码,文件顶部是类似函数的宏HASH:

#define HASH(fp) (((unsigned long)fp)%NHASH)
Run Code Online (Sandbox Code Playgroud)

这让我想知道,为什么有人会选择使用类似函数的宏来实现这种函数,而不是将它作为常规的vanilla C函数实现?每种实施的优缺点是什么?

谢谢你!

Bob*_*phy 24

这样的宏可以避免函数调用的开销.

看起来似乎并不多.但在您的示例中,宏将变为1-2个机器语言指令,具体取决于您的CPU:

  • 获取fp的内存值并将其放入寄存器中
  • 取寄存器中的值,按固定值进行模数(%)计算,并将其保留在同一寄存器中

而功能相当的将是更多的机器语言指令,通常是类似的

  • 将fp的值粘贴在堆栈上
  • 调用该函数,该函数还将下一个(返回)地址放在堆栈上
  • 也许在函数内部构建一个堆栈框架,具体取决于CPU架构和ABI约定
  • 从堆栈中获取fp的值并将其放入寄存器中
  • 取寄存器中的值,按固定值进行模数(%)计算,并将其保留在同一寄存器中
  • 也许从寄存器中获取值并将其放回堆栈,具体取决于CPU和ABI
  • 如果构建了堆栈框架,请将其展开
  • 将返回地址弹出堆栈并继续执行指令

还有更多代码,嗯?如果你正在做一些事情,比如在GUI中的窗口中渲染成千上万个像素中的每个像素,那么如果使用宏,事情运行得更快.

就个人而言,我更喜欢使用C++内联作为更具可读性和更不容易出错的内容,但内联也更多地是对编译器的暗示,它不必采取.预处理器宏是编译器无法争辩的大锤.

  • 当然,C99现在已经十年了.任何不支持它的C编译器都会严重过时. (7认同)
  • @ liw.fi:告诉微软! (6认同)
  • 这就是内联函数的用途!:) (5认同)

AnT*_*AnT 16

基于宏的实现的一个重要优点是它不依赖于任何具体的参数类型.C语言中的函数式宏在很多方面都是C++中的模板函数(C++中的模板诞生为"更文明"的宏,BTW).在这种特殊情况下,宏的参数没有具体类型.它可能绝对是任何可转换为类型的东西unsigned long.例如,如果用户如此满意(并且如果他们愿意接受实现定义的结果),他们可以将指针类型传递给此宏.

无论如何,我不得不承认这个宏不是宏的类型独立灵活性的最好例子,但总的来说,灵活性经常被派上用场.同样,当某个功能由某个功能实现时,它仅限于特定的参数类型.在许多情况下,为了将类似的操作应用于不同的类型,有必要提供具有不同类型的参数的几个函数(和不同的名称,因为这是C),而同样可以通过仅一个类似函数的宏来完成.例如,宏

#define ABS(x) ((x) >= 0 ? (x) : -(x))
Run Code Online (Sandbox Code Playgroud)

与所有算术类型的作品,而基于函数的实现必须提供相当多的人(我暗示标准abs,labs,llabsfabs).(是的,我知道传统上提到的这种宏的危险.)

宏并不完美,但关于"由于内联函数而不再需要类似函数的宏"的流行格言只是简单的无稽之谈.为了完全替换类似函数的宏,C将需要函数模板(如在C++中)或至少需要函数重载(如在C++中那样).如果没有那个类似函数的宏,并且仍将是C中非常有用的主流工具.


pav*_*kha 3

一方面,宏很糟糕,因为它们是由预处理器完成的,预处理器不理解任何有关语言的信息并且会进行文本替换。他们通常有很多限制。我看不到上面的内容,但通常宏是丑陋的解决方案。

另一方面,它们有时甚至比static inline方法更快。我对一个短程序进行了大量优化,发现调用static inline方法所需的时间大约是宏的两倍(只是开销,而不是实际的函数体)。