【问题标题】:When and why use a #define macro(x) instead of function?何时以及为什么使用#define 宏(x)而不是函数?
【发布时间】:2015-02-19 23:07:09
【问题描述】:

看到this question 让我想知道为什么这种方法(玩具示例):

#define foo(x) bar[x] = 0

会比函数更受欢迎:

void foo(unsigned x){ bar[x] = 0; }

在上面链接的问题之前,我之前只在 PolarSSL 库中看到过一次,我认为它是某种优化,并尽量不去想太多。

我假设使用预处理器宏将“调用”替换为“(非)函数体”,无论它存在于何处;而void 函数可能会或可能不会被编译器优化,因此可能会导致一两个小而简单的操作产生大量分支。

还有其他好处吗?

什么时候首选宏方法,什么时候更信任编译器?

【问题讨论】:

  • 标记了 C 和 C++,以防部分风格的问题可能会有所不同。
  • 不是首选。当编译器还没有好的优化器时,这些宏就被ab/使用了。
  • 或在 C++ 和 C99 中的 inline specifier 之前。
  • @HansPassant 啊,好吧-我会继续避开自己:)
  • 并非所有可以用宏完成的事情都可以用函数完成。了解宏可以做什么,但是当一个函数可以完成相同的事情时使用一个函数。不需要完全避免它们。

标签: c++ c macros


【解决方案1】:

首先,我希望你的宏实际上是:

#define foo(x) do { bar[x] = 0; } while (0)

正确的semicolon swallowing

支持宏的一件事是因为您认为编译器的优化器不够好。你可能错了。但是,如果您真的仔细查看了输出并知道自己在做什么,那么您可能是对的,这就是为什么它经常在 Linux 内核中使用。

另一个原因是宏是无类型的,所以你可以这样做:

#define foo(x,t) do { t[x] = 0; } while (0)

适用于任何类型t。缺乏类型检查通常是一个缺点,但在定义您想使用任何类型的内容时它会很有用。

【讨论】:

  • 由于模板,“其他”原因在 C++ 中已过时。
  • 我从来没有用过,我只是编造的,所以很遗憾我被分号噎住了。
  • 当然可以,但这是标记为 CC++
  • @5gon12eder 我也询问了 C - 因为我对这样的差异/双方都感兴趣。
  • 是的,但这也是一个问题,这就是我留下评论的原因。在 C 中合理使用宏在 C++ 中可能是一种糟糕的做法。也许我应该将其表述为:“在 C++ 中,由于模板,其他用途已过时。”
【解决方案2】:

仅仅为了让代码更快而定义宏是没有用的。一个好的编译器会内联 函数调用。但是,当您需要将其结果用作常量时,宏会很有用。

#define ENCODED(a,b,c,d) (((((((a)<<8)+b)<<8)+c)<<8)+d)

switch (a) {
   case ENCODED('f','o','o','0'):   ...
   case ENCODED('b', 'a', 'r', '1'): ...
}

当你想定义新的标识符时:

#define LIB_VERSION v101
#define VERSIONNED(x) x##LIB_VERSION

void VERSIONNED(myfunction)(int x) { ... }

当你想做一些其他的“魔法”时,。例如:

#define assert(x) {if ((x) == 0) {printf("%s:%d: Assertion %s failed\n", __FILE__, __LINE__, #x); exit(-1); }}

当您想定义一个使用多种类型的“通用”函数时。仅作说明:

#define DELETE_LAST_ITEM(x) {while (x->next->next != NULL) x=x->next ; x->next = NULL}

可能还有一些我现在不记得的其他情况。

【讨论】:

  • 常量结果是在 C 中使用类函数宏的正当理由。在 C++ 中,这不是必需的,并且被认为是一件坏事。 constexpr 可以在任何需要编译时常量的地方使用。模板可以帮助使其适用于任意类型。
【解决方案3】:

还有其他好处吗?

使用宏在情境方面的好处很少。仅举个例子,您可以使用__LINE____FILE__ 来查看调用此宏进行调试的位置。

#define foo(x) bar[x] = 0; PrintFunction("...", __FILE__,__LINE__)

宏永远不会像函数那样为您提供更强大的类型检查。

什么时候首选宏方法,什么时候更信任编译器?

因此,只有在您别无选择使用函数时,才应该首选宏,因为大多数时候您可能会信任编译器优化器。

【讨论】:

    猜你喜欢
    • 2015-04-29
    • 2017-07-25
    • 2011-08-25
    • 2020-05-23
    • 2014-10-06
    • 1970-01-01
    • 2011-01-21
    • 1970-01-01
    相关资源
    最近更新 更多