6

C/C++ 中不乏不良或危险的类似函数的宏的示例。

#define SQUARE(x) x * x
printf("%d", SQUARE(4));   //16
printf("%d", SQUARE(3+1)); //7
#undef SQUARE

#define SQUARE(x) (x) * (x)
printf("%d", 36/4);                //9
printf("%d", SQUARE(6)/SQUARE(2)); //36
#undef SQUARE

#define SQUARE(x) ((x) * (x))
int x = 3;
++x;
printf("%d", SQUARE(x)); //16

int y = 3;
printf("%d", SQUARE(++y)); //?
#undef SQUARE

鉴于类函数宏的问题,它们有哪些好的/谨慎/推荐使用的例子?

是否有任何时候类似函数的宏比函数更可取?

我想这一定有一些非常好的案例,否则预处理器制造商会让他们的工作变得更容易而将它们排除在外。

4

6 回答 6

3

评论已经占据了大部分。

某些类型的调试和检测只能通过使用函数式宏来完成。最好的例子是assert(),但函数样式的宏也可以用于检测代码以进行分析。__FILE__像和__LINE__一样的魔术宏以及#引用和##标记粘贴等功能使类似函数的宏对于调试和分析很有价值。

在 C++ 中,使用模板和通常更积极的内联,几乎没有(如果有的话)使用函数式宏的其他理由。例如,由于问题中说明的原因,模板函数std::max是比宏更好的解决方案。MAX

在 C 中,有时需要在优化中确保一小段代码是内联的。带有所有警告的宏在这种情况下仍然偶尔有用。ALLCAPS 命名约定是为了警告程序员这实际上是一个宏而不是一个函数,因为简单的文本替换存在所有问题。例如,如果您有几个地方需要std::max在性能关键的代码段中等效,那么宏(及其所有危险)可能是一个有用的解决方案。

于 2012-09-09T18:37:02.253 回答
2

大多数时候你不需要使用宏。然而,有些情况是合法的(尽管有讨论的余地)。

当您可以使用枚举时,您不应该使用宏。您不应该拥有依赖于具有魔术名称的局部变量的宏。你不应该有可以用作左值的宏。您不应该有对扩展宏周围的代码有副作用的宏。在大多数情况下,使用宏而不是内联函数是一个坏主意,无论如何,列表是无穷无尽的。

但是,您可以使用宏来伪造迭代器,在 C 中,特别是在 Linux 内核中,您将看到以下内容:

#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
     pos = n, n = pos->next)

Linux 内核源代码中使用了许多其他类似类型的宏。

此外,offsetof(3) 和 assert(3) 等通常被实现为宏。

于 2012-09-09T18:34:55.263 回答
1

我一般同意这里的 dmp 。如果您可以使用其他东西,则可以使用它并忘记宏。

也就是说,我将主要重复评论中的内容:

  1. 当您需要在其中使用一些其他预处理器符号(__LINE__、__FILE__、__func__ 等)时,通常需要类似宏的函数。这通常意味着断言/调试宏。
  2. 在宏中字符串化/粘贴标记(# 和 ## 运算符)。
  3. 你可以(如果你足够冒险的话)使用类似函数的宏来创建枚举->字符串映射,使用一些切割技巧,如定义/取消定义和重新定义宏。
  4. 像 va_list/va_arg 这样的东西,您需要通过指针访问并同时更改它指向的位置。

我再重复一遍——如果你能避免预处理器,就这样做。

于 2012-09-09T18:48:01.937 回答
0

C标准有(或允许)一大堆它们,我认为任何以同样的精神做宏的东西都应该是合法的:

  • 字符处理函数isspace和类似函数通常作为宏实现
  • CMPLX被声明为宏
  • UINT64_C和类似的是宏
  • 如果你包含tgmath.h,sincos许多其他函数成为宏
  • new_Generic关键字的全部意义在于编写类似函数的宏
于 2012-09-09T18:44:38.943 回答
0

在某些情况下,将有编译器特定的指令,这些指令会有所不同并且对于每个编译器都需要重写。我经常使用的一个属于这一类的宏用于通知编译器一个参数未使用:

virtual size_t heightForItemAtIndex(const size_t& idx) const {
  MONUnusedParameter(idx); // << MONUnusedParameter() is the macro
  return 12; // << this type's items are all the same height
}

问题在这里介绍得比较好:cross platform macro for silencing used variables warning

于 2012-09-09T18:45:20.237 回答
0

Google Style C++ Guide有一个非常有用的宏示例(我不确定这是否是您所说的“类函数宏”的意思——它需要一个参数,但不能被函数调用替换):

DISALLOW_COPY_AND_ASSIGN(类名);

这样的宏可以放在类的私有部分中,以禁用由编译器自动生成的复制构造函数和赋值运算符。除非类确实需要它们,否则禁止这两个自动生成的方法通常是个好主意。如果没有禁用宏,则需要在每个类中输入大量内容:

类名(常量类名&);
无效运算符=(常量类名&);

宏只是自动生成此代码。这是相当安全的。我不知道这种宏会导致问题的任何情况。

于 2012-09-09T20:28:03.217 回答