C中不同宏函数/内联方法的优缺点

C中不同宏函数/内联方法的优缺点,c,macros,inline,C,Macros,Inline,,C中“内联”代码基本上有3种实用方法: #define MACRO(arg1, arg2) do { \ /* declarations */ \ stmt1; \ stmt2; \ /* ... */ \ } while(0) /* (no trailing ; ) */ 或 为了澄清这一点,表达式中使用了参数,逗号运算符返回最后一个表达式的值 或 使用内联声明,该声明作为 do{…}while(0)方法在Linux内核中广泛使用,但

,C中“内联”代码基本上有3种实用方法:

#define MACRO(arg1, arg2) do { \
    /* declarations */ \
    stmt1;   \
    stmt2;   \
    /* ... */  \
    } while(0)    /* (no trailing ; ) */

为了澄清这一点,表达式中使用了参数,逗号运算符返回最后一个表达式的值

使用
内联
声明,该声明作为

do{…}while(0)
方法在Linux内核中广泛使用,但我很少遇到其他两种方法

我特别指的是多语句“函数”,而不是像MAX或MIN这样的单语句函数


每种方法的优缺点是什么?在各种情况下,为什么选择一种方法而不是另一种方法?

我在使用任何构造时看到的唯一优点是加快代码速度

因此,选择一种提供最快代码的方法

如果都一样的话,我会觉得更清晰

  • “标准”功能
  • 内联
    函数
  • #定义。。。执行{}while(0)
    approach
  • 使用逗号分隔表达式的宏

  • 使用inline关键字的一个优点是通过函数原型检查参数的类型。使用宏时,你不会得到这样的结果,所以如果你把错误类型的东西放在宏中,宏很容易产生奇怪的错误。(虽然不像C++中的模板错误那么可怕) 使用宏的一个优点是,您可以使用#arg执行一些有趣的操作,例如连接和将宏参数转换为字符串。使用预处理器宏的另一个好处是,您可以使用cpp轻松检查它们如何展开以展开它们。这就是调试这些错误的方法


    宏定义函数的另一个有用之处是,如果需要,可以在函数中插入
    return
    语句来停止父函数。对于内联函数,您必须返回一个值,然后检查返回值。

    谈到宏的具体用途,即充当“函数”的宏,我要提到内联函数中无法具有的宏的以下优点:

    惰性参数评估。例如,像这样的宏

    #define SELECT(f, a, b) ((f) ? (a) : (b))
    
    将保留三元运算符的惰性参数求值属性:仅对选定参数求值,而不对另一个参数求值。一个简单的内联函数模拟将提前计算这两个参数,从而完成额外的不必要的工作

    访问上下文。宏可用于实现“局部函数”的某些相似性,即可访问局部变量和封闭函数参数的重复代码段

    类型独立性(和类型参数)。宏允许您编写与类型无关的“函数”(参见上面的示例)。如果不能摆脱类型依赖,可以将类型作为参数传递给宏


    宏的上述属性(我将其作为优点介绍)可能被误用以导致重大故障(因此也可能被作为缺点介绍)。但是对于C语言中的许多语言功能来说,这是可以说的。

    中间的例子没有太多意义,宏使用名为arg1和arg2的参数,在宏体中使用expr1、expr2和expr3?这是有意义的,因为
    argN
    可以嵌套在右侧的子表达式中。我想,就像
    (arg1^=arg2,arg2^=arg1,arg1^=arg2)
    。除非您已经测量了代码并知道需要优化,否则不要麻烦:为所有内容编写普通函数。@litb:Aha,我想这是有道理的。谢谢。@unwind,第二个将使用表达式中的宏参数。第一种方法创建一个块,允许宏位于以分号结尾的行上。第二个可以用来代替值(表达式3返回使用的值)。第三个使用编译器的扩展。在大多数情况下,我会选择第一个,因为第二个在使用上更受限制。第三个我不会使用,因为移动到另一个操作系统/编译器/等可能意味着失去该特定扩展。在像#define MAX(A,B)((A)>(B)?(A):(B))这样的例子中,如果A或B包含一个动作(如I++),它甚至是最糟糕的,因为它将被计算两次。@AndreyT:也许你可以说出其中的一些缺点?@eyalm:实际上,我认为当一个变量在一个表达式中被多次修改时,行为是未定义的,除非您使用短路运算符(&&,| |,','),')来引入序列点。顺便说一句,多重求值问题适用于任何语句,包括我相信的函数调用,即foo(I++,I++)也会给出未定义的行为,因为您不知道函数参数的求值顺序。@AndreyT我感兴趣的是如何将类型作为参数传递给宏?我不熟悉C99中的这种功能?事实上,我有点希望函数模板在C中可用。这样你就可以通过函数的类型安全性获得宏的灵活性。@RobertS.Barnes,之所以称为C是有原因的
    #define SELECT(f, a, b) ((f) ? (a) : (b))