为什么在C中使用宏?

Ala*_*orm 48 c macros

可能重复:
什么是C宏有用?

每隔几个月我就会学到一些C,这是我的废话大学编程教育从未涉及过的.今天它是宏.我对宏的基本理解是它们是一个简单的搜索和替换,它会在编译之前发生在代码上.我无法理解你为什么要使用宏.我正在看的大多数基本例子都是这样的

TEST(a,%d);
#define TEST(a,b) printf(" The value of " #a " = " #b " \n", a)

//which expands to 
printf(" The value of a = %d \n",a);
Run Code Online (Sandbox Code Playgroud)

(例子来自这里)

从我的新手角度来看,似乎定义一个新函数会给你相同的结果.我可以看到历史上宏如何在易于搜索和替换之前的几天内快速修改大量源代码,但有些东西告诉我,我错过了一些更重要的观点.

那么宏可以为你做什么样的有用的事情呢?

Tyl*_*nry 65

这不是搜索和替换,它是令牌扩展.C宏是计算世界中每一种其他类型的宏:一种简短而简单的方法,可以让它自动变成更长,更复杂的东西.

使用宏的一个原因是性能.它们是一种消除函数调用开销的方法,因为它们总是在线扩展,不像"内联"关键字,这是编译器经常被忽略的提示,并且在C99之前甚至不存在(在标准中).例如,请参阅与select和pselect使用的fd_sets一起使用的FD_系列宏.这些fd_sets实际上只是位集,FD_宏隐藏了位操作.每次写出自己的比特都会很烦人,如果没有内联,那么函数调用对于如此快速的操作来说将是很多开销.

此外,宏可以做一些功能不能做的事情.考虑令牌粘贴.由于预处理器在编译器之前运行,因此它可以为编译器创建新的标识符.这可以为您提供创建大量类似定义的简便方法,例如

#define DEF_PAIR_OF(dtype) \
  typedef struct pair_of_##dtype { \
       dtype first; \
       dtype second; \
  } pair_of_##dtype##_t 

 DEF_PAIR_OF(int);
 DEF_PAIR_OF(double);
 DEF_PAIR_OF(MyStruct);
 /* etc */
Run Code Online (Sandbox Code Playgroud)

它可以做的另一件事是函数无法将编译时信息转换为运行时信息:

#ifdef DEBUG
#define REPORT_PTR_VALUE(v) printf("Pointer %s points to %p\n", #v, v)
#else 
#define REPORT_PTR_VALUE(v)
#endif

void someFunction(const int* reallyCoolPointer) {
  REPORT_PTR_VALUE(reallyCoolPointer);
  /* Other code */
}
Run Code Online (Sandbox Code Playgroud)

函数无法像输出宏一样在其输出中使用其参数的名称.这也证明了为发布版本编译调试代码.


Deu*_*uro 45

一个原因是直到C99,内联关键字在C语言中不是标准的.因此,宏允许您内联小函数.它们在某些方面也像模板一样工作,即.您不必在宏定义中指定类型,例如:

#define MAX(x,y) ((x) > (y) ? (x) : (y))
Run Code Online (Sandbox Code Playgroud)

这个宏适用于整数,双精度,浮点数等.

  • 你应该在右边的a和b周围有parens. (2认同)
  • 这是个好的观点.宏不会创建额外的堆栈帧(当然,除非您从一个函数调用一个函数). (2认同)
  • Microsoft的编译器允许您说__forceinline来覆盖编译器.但你可能永远不应该使用它.除非你是那种能够看到拆卸并且说某些人比另一个更好的人.http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx (2认同)

Mik*_*aid 16

宏在编译时扩展.你是正确的,他们经常被滥用,但C中的一个经典例子是有一个用于编写调试消息的宏(当编译时关闭调试模式时)不生成任何代码,因此不会导致减速.

  • 这是实现细节.原则上,这可以作为编译的两个阶段(如果我们看得更深,几乎任何现实生活中的编译器都会在很多阶段完成工作). (4认同)
  • "宏在编译时扩展." 预处理器在编译器甚至看到任何代码之前扩展宏. (3认同)

i_a*_*orf 12

有时您想要记录,但仅在调试模式下.所以你写了类似的东西:

#ifdef DEBUG
#define LOG_MSG(x) printf(x);
#else
#define LOG_MSG(X)
#endif
Run Code Online (Sandbox Code Playgroud)

有时你只想根据编译时开关关闭或打开一些东西.例如,我的公司对我们的产品做了一些联合品牌,合作伙伴要求解决问题.所以我们会做类似的事情:

#ifndef SPECIAL_PARTNER_BUILD
    DoSomethingReallyAwesome();
#endif
Run Code Online (Sandbox Code Playgroud)

然后在给伙伴丢弃时使用-DSPECIAL_PARTNER_BUILD构建.

还有许多其他可能的原因.

有时你只想为你一遍又一遍的事情保存打字.有些人认为这很远(咳嗽 MFC 咳嗽)并在Macros中写出相当于他们自己语言的内容来抽象出一个困难的API.这使得调试成为一个frikkin'的噩梦.


lil*_*llq 6

除了功能之外,宏可以有许多不同的用途.

将宏用于任何幻数或字符串相关非常有用.

#define MY_MAGIC_NUM 57

/*MY_MAGIC_NUM used all through out the code*/
Run Code Online (Sandbox Code Playgroud)

  • 不,全局常量变量和宏之间存在明显差异.也就是说,宏的值在编译之前被代入代码中,而全局常量的值存储在可执行文件中并加载到内存中并在执行时检索(尽管有优化).您可以获取全局常量的地址,但不能获取宏的地址. (2认同)

Dou*_*rch 5

C 宏可以在编译时生成代码。这可以(ab)用于有效地创建具有新关键字和行为的特定领域语言。

我最喜欢的例子之一是 Simon Tatham通过宏在 C中实现协程的方法。最简单的宏实现是:

#define crBegin static int state=0; switch(state) { case 0:
Run Code Online (Sandbox Code Playgroud)

是的,有一个无与伦比的支架。其他宏会解决这个问题。