C语言宏定义的使用

宏定义采用define关键字进行定义,是简单的字符串替换,主要分有参数和无参数两种。这里就平常碰到的使用方法做一个总结,方便以后查看。
1、#define MAX_NUM 1000 普通宏定义,最大值为1000
2、#define  NULL_PTR  ((void*)0) 定义空指针
3、用宏定义断言(assert)

#define  ASSERT(condition, message) {\if(!(condition)){\logError("Assertion failed:",#condition,message);\exit(EXIT_FAILURE);\}\
}

4、防止一个头文件被重复包含,在比较大的工程中经常使用 #ifndef  _DCS_CONTAINER_H_ #define _DCS_CONTAINER_H_

#endif
5、带参数的,例如:求最大最小值 #define   MAX( x, y )  ( ((x) > (y)) ? (x) : (y) ) 
#define   MIN( x, y )  ( ((x) < (y)) ? (x) : (y) ) 其实上面这两种写法不是特别好,在参数x,y带有运算性质的时候容易出错: 举例:
float a = 1.0f;
float b = MIN(a++, 1.5f);
// => float b = ((a++) < (1.5f) ? (a++) : (1.5f))
// => a=3.000000, b=2.000000  //实际输出结果(应该跟你期望的不一样吧)
推荐:GUN C中MIN的写法:
#define MIN(A,B)    ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })
这里定义了三个语句,分别以输入的类型申明了__a和__b,并使用输入为其赋值,接下来做一个简单的条件比较,得到__a和__b中的较小值,并使用赋值扩展将结果作为返回。这样的实现保证了不改变原来的逻辑,先进行一次赋值,也避免了括号优先级的问题,可以说是一个比较好的解决方案。
6、预定义宏
ANSI C标准中有几个标准预定义宏(也是常用的):
__LINE__:在源代码中插入当前源代码行号;
__FILE__:在源文件中插入当前源文件名; __FUNCTION__: 在源文件中插入当前源函数名;
__DATE__:在源文件中插入当前的编译日期
__TIME__:在源文件中插入当前编译时间;
__STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;
__cplusplus:当编写C++程序时该标识符被定义。 代码:
int main(int argc, char** argv) 
{printf("hello world!\nfile = %s \nfunction = %s \nline = %d \ndate = %s \ntime = %s",__FILE__, __FUNCTION__, __LINE__, __DATE__, __TIME__);return 0;
}

运行结果:
7、可变参数宏:__VA_ARGS__:
可变参数宏不被ANSI/ISO C++ 所正式支持。C99编译器标准允许你可以定义可变参数宏(variadicmacros),这样你就可以使用拥有可以变化的参数表的宏。可变参数宏就像下面这个样子:
 #define DEBUG(...) printf(__VA_ARGS__)
缺省号代表一个可以变化的参数表。使用保留名__VA_ARGS__把参数传递给宏。当宏的调用展开时,实际的参数就传递给 printf()了。例如:
 DEBUG("Y = %d\n", y);
而处理器会把宏的调用替换成:
 printf("Y = %d\n", y);
GCC中同时支持如下的形式
#define LOG(format, ...) fprintf(stdout, format, __VA_ARGS__) LOG("hello\n", 0);    输出:hello 不过,这样写会有一个问题,就是当你不输入后面的参数的时候编译会不过。 例如:LOG(“hello\n"); 后面没有跟参数,编译失败 原因:编译后变成    fprintf("hello\n", );   多了一个逗号!! 有解决方法么?有。”##“就要派上用场了。 ##符号在 逗号 和 参数名 之间时不做连字符作用,而作为变参宏的特别用处,简单理解:可以吃掉前面的空格和逗号
小技巧:
#ifdef DEBUG
#define LOG(format, ...) fprintf(stdout, ">> "format"\n", ##__VA_ARGS__)
#else
#define LOG(format, ...)
#endif
在调试环境下,LOG宏是一个变参输出宏,以自定义的格式输出;
在发布环境下,LOG宏是一个空宏,不做任何事情。
#define LOG(format, ...) fprintf(stdout, format, ##__VA_ARGS__)
#define LOG(format, args...) fprintf(stdout, format, ##args)
"##"的作用是对token进行连接,在上例中,format、__VA_ARGS__、args即是token,如果token为空,那么不进行连接,所以允许省略可变参数(__VA_ARGS__和args)
当__VA_ARGS__是空参数时,##运算符会把前面的逗号吃掉。
##运算符把前后两个预处理Token连接成一个预处理Token,并且变量式宏定义中也可以用##运算符

8、特殊符号: # #将其后面的宏参数进行字符串化操作。 #define LOG(module)  fprintf(stderr, "error: "#module"\n") LOG(I = 0) 输出:error: I = 0
9、特殊符号: ## ## 是一种分隔连接方式,先分隔,再进行强制连接。"##"的作用是对token进行连接,在前面的例子中中,format、__VA_ARGS__、args即是token,如果token为空,那么不进行连接,所以允许省略可变参数(__VA_ARGS__和args)
当__VA_ARGS__是空参数时,##运算符会把前面的逗号吃掉。(前面已经给例子了)
##运算符把前后两个预处理Token连接成一个预处理Token,并且变量式宏定义中也可以用##运算符
#define NAME(n) x ## n
#define PRINT_N(n) printf("x" #n " = %d/n", x ## n);int NAME(1) = 66; // becomes int x1 = 66;int NAME(2) = 88; // becomes int x2 = 88;PRINT_N(1); // becomes printf("x1 = %d/n", x1);PRINT_N(2); // becomes printf("x2 = %d/n", x2);

#define LOG(format, ...) fprintf(stdout, format, ##__VA_ARGS__)
#define LOG(format, args...) fprintf(stdout, format, ##args)
10、宏定义中的 do{} while(0)语句 使用do{….}while(0) 把整个执行部分包裹起来,使该宏成为一个独立的语法单元,从而不会与上下文发生混淆。 例如:
#define action(x,y) {do something}if(x>y)action(x,y);else otheraction();
在action(x,y);  处编译的时候出错了,因为多了一个分号。上述宏如果换成如下就没问题:
#define action(x,y) \do{ \do something; \} while(0)



本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部