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)


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值