为什么在C中使用macros?
可能重复:
什么是Cmacros有用?
每隔几个月,我都会痒痒地去学习一些C语言,那就是我的垃圾大学编程教育从来没有涉及过。 今天是macros。 我对macros的基本理解是,它们是一个简单的search,并在代码被编译之前replace它。 我无法理解为什么要使用macros。 我正在看的大部分基本例子都是这样的
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);
(从这里例子)
从我的新手angular度来看,似乎定义一个新的function会给你相同的结果。 我可以看到历史上的macros如何在易于search和replace之前快速修改大量源代码很有用,但是某些事情告诉我我错过了一些更重要的点。
那么macros可以为你做些什么有用的事情呢?
原因之一是直到C99,inline关键字在C语言中不是标准的。 因此,macros允许你内联小函数。 他们也在某些方面像模板一样工作,即。 您不必在macros定义中指定types,例如:
#define MAX(x,y) ((x) > (y) ? (x) : (y))
这个macros是与整数,双打,浮游物等complient。
这不是完全search和replace,这是令牌扩展。 Cmacros是计算机世界中所有其他types的macros:一种写简单而简单的方法,并自动变成更长更复杂的东西。
macros使用的一个原因是性能。 它们是一种消除函数调用开销的方法,因为它们总是在内部扩展,与“inline”关键字不同,它是编译器经常忽略的提示 ,在C99之前甚至不存在(在标准中)。 例如,请参阅与select和pselect使用的fd_sets一起使用的FD_系列macros。 这些fd_sets实际上只是位集,而FD_macros则隐藏了位操作。 如果每次都写出自己的位,那么写这个函数会很烦人,如果没有内联的话,函数调用对于如此快速的操作来说会是很大的开销。
另外,macros可以做一些function不能的东西。 考虑令牌粘贴。 由于预处理器在编译器之前运行,因此可以为编译器创build新的标识符。 这可以给你一个简单的方法来创build很多类似的定义,例如
#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 */
另一件可以做的事情是函数不能将编译时信息转化为运行时信息:
#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 */ }
没有办法,一个函数可以使用其参数的名称,如macros可以输出。 这也演示了编译发布版本的debugging代码。
macros在编译时被扩展。 你是正确的,他们经常被滥用,但在C中的典型例子是有编写debugging信息的macros(编译时closuresdebugging模式时)不会产生任何代码,所以不会导致减速。
有时你想做日志logging,但只能在debugging模式下进行。 所以你写这样的东西:
#ifdef DEBUG #define LOG_MSG(x) printf(x); #else #define LOG_MSG(X) #endif
有时你只是想根据编译时间开关来closures或打开某些东西。 例如,我的公司对我们的产品进行了一些联合品牌推广,并且合作伙伴要求closures这些产品。 所以我们会做这样的事情:
#ifndef SPECIAL_PARTNER_BUILD DoSomethingReallyAwesome(); #endif
然后在给予伙伴丢弃时使用-DSPECIAL_PARTNER_BUILD构build。
在许多其他可能的原因。
有时候,你只是想保存一遍又一遍地重复input。 有些人把这个( 咳嗽 MFC 咳嗽 ),并在macros中写下自己的语言来抽象出一个困难的API。 这使得debuggingfrikkin的噩梦。
除了函数之外,macros可以有许多不同的用途。
使用任何与魔术数字或string相关的macros是非常有用的。
#define MY_MAGIC_NUM 57 /*MY_MAGIC_NUM used all through out the code*/
Cmacros可以在编译时生成代码。 这可以用来(ab)用新的关键字和行为有效地创build特定于领域的语言。
我最喜欢的例子之一是Simon Tatham用C语言实现协程的方法。
在性能激烈的情况下,您可以使用macros创build“自动”循环展开。 现代编译器可能在这方面做得比较好,但是过去这是一个有用的应用程序。
编译器通常知道关于目标机器的细节,所以你可以使用条件编译为大端处理器和小端处理器分配一个代码。 这样可以防止代码被devise用于不同处理器的代码臃肿。
类似的情况是,当你有一个特定的系统的汇编代码,而其他系统的C代码。
我们在代码中使用了这种types的macros:
// convenience macros for implementing field setter/getter functions #ifndef CREATE_GET_SET #define CREATE_GET_SET(PREFIX, FIELD_NAME,FIELD_DATATYPE) \ protected:\ FIELD_DATATYPE PREFIX ## FIELD_NAME;\ public:\ inline FIELD_DATATYPE get ## _ ## FIELD_NAME(void) const\ { \ return(PREFIX ## FIELD_NAME); \ } \ inline void set ## _ ## FIELD_NAME(FIELD_DATATYPE p) \ { \ PREFIX ## FIELD_NAME = p; \ } #endif
在一个类/结构中你可以定义一个variables:
CREATE_GET_SET(_, id, unsigned int);
这将定义你的variables,并为代码创build通用的getter / setter。 它只是为get / set生成更清晰,一致的代码。 当然,你可以把它全写出来,但是这是很多样板代码注意:这只是一对夫妇的macros之一。 我没有发布全部。 你不会用这种方式来处理“char *”(你想要设置为strncpy或strcpy数据)。 这只是一个简单的演示,你可以用macros和一些简单的types来做什么。
我觉得最大的好处就是用作“设置文件”
#define USE_FAST_AGORHITM //#define USE_SLOW_ONE
而全球“常数”
#define MAX_NUMBER_OF_USERS 100000
有一些程序主要是用macros编写的(例如,检查Brian Gladman的AES实现http://www.gladman.me.uk/cryptography_technology/index.php )
在macros中,函数的代码被插入到调用者的代码stream中。 这可以,取决于许多其他的事情,提高性能,因为优化程序可以程序化地集成被调用的代码 – 优化被调用的代码到调用者但要小心,因为macros上的参数types不检查。 内联函数(如果您也使用C ++)和一点macros的一个很好的参考是。 http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.5