1

就像在 linux 内核源代码中一样,我发现很多函数都是用内联形式实现的,还有很多其他的函数也是用 MACRO 实现的。

所以我想知道在决定选择哪一个时所涉及的考虑是什么,因为我认为两者都是合适的。或者这只是一种个人风格?

4

3 回答 3

1

宏不进行类型检查并且需要其他特殊注意事项(即只对表达式求值一次),因此应尽可能避免使用它们。但是,不检查类型的优点是允许您在 C89 中编写泛型函数。

编写真正的inline函数还允许(使用可以在内核配置中激活的某个标志)编译器也可以取消内联它,如果这具有性能优势,而这对于宏来说是不可能的,因为它是一个简单的文本替换。

于 2013-01-05T10:40:50.503 回答
0

作为文本替换的宏可以做的不仅仅是内联函数。它们也可能有不必要的副作用。考虑众所周知的交换宏。

#define SWAP(a,b) {int t=a; a=b; b=t}

并注意到SWAP(i,t[i])做不想要的事情。

内联函数具有类似语义的函数调用的优点(参数被评估一次,等等......)。

所以这主要是编码风格的问题,以及认识到宏和函数是不同种类的动物。

而且您的问题与内核并没有真正相关。任何 C(甚至 C++)程序都有同样的问题。

于 2013-01-05T10:41:19.617 回答
0

请记住,Linux 内核中的许多代码已经存在了很长一段时间。一些宏还对输入/环境的其他部分做“聪明的事情”,而不是函数会/可以/应该做的事情。

作为一般规则,尽可能使用(内联)函数,因为函数的“陷阱”较少,如上所述。

如果您进行巧妙的替换或其他一些“只能在宏中执行”,那么如果可能的话,最好制作一个形成函数参数的宏。例如

#define ASSERT(x)  do { if (!(x)) { fprintf(stderr, "Assertion failed on %s:%d", __FILE__, __LINE__); exit(1); } while(0);

可以这样做:

#define ASSERT(x) do_assert(!!(x), __FILE__, __LINE__);

void do_assert(bool val, const char *file, int line)
{
    if (!val)
    {
        fprintf(....);
        exit(1);
    }
}

例如,现在您可以在调试构建中,在 if 内的断言中设置断点,并且每当您的断言失败时,您都可以看到您是如何到达那里的。使用宏,这是不可能的。

如果有一半的机会,现代编译器在适当的时候内联事物方面做得非常好。

于 2013-01-05T11:05:05.443 回答