【问题标题】:What are the benefits of using macros instead of functions in C?在 C 中使用宏而不是函数有什么好处?
【发布时间】:2021-05-10 20:04:19
【问题描述】:

第一个密码:

#include <stdio.h>
 
int area(int a,int b){
        int area1 = a*b;
        return area1;
    }
int main()
{
    
    int l1 = 10, l2 = 5, area2;
 
    area2 = area(l1,l2);
 
    printf("Area of rectangle is: %d", area2);
 
    return 0;
}

第二个密码:

#include <stdio.h>
 
// macro with parameter
#define AREA(l, b) (l * b)

int main()
{
    int l1 = 10, l2 = 5, area;
 
    area = AREA(l1, l2);
 
    printf("Area of rectangle is: %d", area);
 
    return 0;
}

两个代码的输出相同:矩形面积为:50

我的问题:很明显,C 语言中的宏与函数相同,但宏比函数占用的空间更少(行数更少)。这是使用宏而不是函数的唯一好处吗?因为它们看起来大致相同。

【问题讨论】:

  • 通常最好使用内联函数而不是宏。为其他令牌粘贴留下宏。检查这个问题stackoverflow.com/questions/1640344/…
  • 尝试将AREA( x + y, a + b ) 用作宏和函数...
  • Re“空间效率以外的”,由于每次使用都是“内联”,使用宏会占用更多空间。
  • “很明显,C 语言中的宏和函数是一样的”——这不是“显而易见的”,因为它不是真的。采取“更少的行” 并不是优势。那只是打字而已,在任何情况下都不一定正确。 int area(int a,int b){return a * b;} - 一行,没有不必要的变量,类型安全,即使是最普通的优化器也可能内联。您的AREA 宏在任何情况下都存在严重缺陷,这也是为什么应避免使用类似函数的宏的一个例子#define AREA(l, b) ((l) * (b))
  • 函数调用曾经更昂贵,而且旧的编译器没有优化它们,所以宏会更有效。当然,在某些情况下,宏会做一些函数不做的事情,例如引用周围范围内的标识符。但是,在现代 C 语言中,静态内联函数通常比宏更受欢迎。

标签: c function macros


【解决方案1】:

宏绝对与函数相同。宏是文本替换1;它们不像函数那样称为

AREA 宏的问题在于,如果您传递像 AREA(l1+x,l2) 这样的 表达式,它将无法正常运行 - 它将扩展为 (l1+x * l2),它不会做什么你要。宏的参数不会被评估,它们会被扩展。

宏和类似函数的宏对于创建符号常量、简化重复的文本块以及实现粗略的类似模板的行为很有用。


  1. 严格来说都是token替换,但是原理是一样的。

【讨论】:

  • 通过在宏定义中的参数周围使用括号,可以避免您显示的评估与扩展问题。不过,除了您提到的原因之外,使用宏而不是函数通常不是一个好主意。
【解决方案2】:

当您将宏与__FILE____LINE__ 结合使用时,宏很有用。

我在Bismon 软件项目中有一个具体的例子。在其文件cmacros_BM.h 我定义

// only used by FATAL_BM macro
extern void fatal_stop_at_BM (const char *, int) __attribute__((noreturn));
#define FATAL_AT_BIS_BM(Fil,Lin,Fmt,...) do {                   \
    fprintf(stderr, "BM FATAL:%s:%d: <%s>\n " Fmt "\n\n",       \
            Fil, Lin, __func__, ##__VA_ARGS__);                 \
    fatal_stop_at_BM(Fil,Lin); } while(0)

#define FATAL_AT_BM(Fil,Lin,Fmt,...) FATAL_AT_BIS_BM(Fil,Lin,Fmt,##__VA_ARGS__)

#define FATAL_BM(Fmt,...) FATAL_AT_BM(__FILE__,__LINE__,Fmt,##__VA_ARGS__)

致命错误正在调用类似(来自file user_BM.c 的示例)

  FILE *fil = fopen (contributors_filepath_BM, "r+");
  if (!fil)
    FATAL_BM ("find_contributor_BM cannot open contributors file %s  : %m",
              contributors_filepath_BM);

fopen 失败时,致命错误消息会显示该FATAL_BM 宏调用的源文件和行号。

fatal_stop_at_BM 函数在file main_BM.c 中定义

还请注意,您的某些 C 文件可能由GNU bisonGNU m4ANTLRSWIG 等程序生成,并且GNU autoconf 也使用预处理器符号.

还要研究Linux kernel 的源代码。它广泛使用宏。

最重要的是,阅读 C 编译器的文档(例如 GCC)。许多 C 编译器可以向您显示 C 代码的预处理形式。


你的

 // macro with parameter
 #define AREA(l, b) (l * b)

如果您希望 AREA(x+2,y-3) 按预期工作,则错误,应该是 #define AREA(l, b) ((l) * (b))

出于性能原因,您可以将函数定义为

inline int area(int a,int b){ return a*b; }

另见:

【讨论】:

    【解决方案3】:

    我同意@Eric Postpischil 的观点。旧的编译器不支持宏,它们对宏一无所知。因此,如果您使用旧的编译器,这会使调试变得更加困难。

    这是使用宏而不是函数的唯一好处吗?

    不,宏是类似函数的,只能用于定义非常简单的东西,例如简单的公式,但不建议用它们定义 C 函数,因为它们试图将所有内容都放在线性和扁平化,这就是为什么您可能会遇到软件设计问题并且它们会使调试变得更加困难。所以,这并不总是一种好处。

    在你的情况下,我认为这不是一个大而严重的问题。

    【讨论】:

    • 所有符合 C89 或更高版本的 C 编译器都支持宏,并且许多早期的编译器直接或通过单独的 C 预处理器支持它们。没有合理的理由担心您今天使用的 C 编译器将无法支持宏。此外,您的散文可以被解读为断言 Eric 另有主张,但我相信他没有,甚至在任何可能已被删除的 cmets 中也没有。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    • 1970-01-01
    • 1970-01-01
    • 2015-12-17
    • 2010-09-14
    • 2022-08-03
    相关资源
    最近更新 更多