C语言宏中"#"和"##"的用法以及宏展开规则

之前也看过一些C语言宏的使用,特别是对_T 以及 TEXT 宏的实现也比较感兴趣,但是之前对_T,TEXT宏的实现也是一知半解,没有彻底搞明白,周末在写另外一篇博客(https://blog.csdn.net/zsc_976529378/article/details/105451453)时恰好又用到了宏,所以想好好把这块学习整理下。

 

  • C语言宏中#的用法

#的作用是把一个token(标记)变成一个字符串,也就是使用""把token包含起来

下面是比较常用的用法

#define NAME_TO_STR(name) (#name)

下面这个可以支持转换成unicode字符串

#define NAME_TO_STR(name) (_T(#name))

_tprintf(NAME_TO_STR(name_to_string)); // 输出字符串"name_to_string" 

 

又比如在前面提到的博客中,为了把windows定义的一些宏名称(CSIDL_APPDATA)打印出来,使用了这个宏

#define MAKE_PAIR(id) (std::pair<int, const TCHAR *>(id, _T(#id)))

 

  • C语言宏中##的用法

##的作用是把两个token贴合在一起,比如下面CONS宏用来定义科学计数法的整数

#define CONS(a,b)  int(a##e##b) 

printf("%d\n", CONS(2,3));  // 2e3 输出:2000

还有一些用法是用来定义变量,比如

#define DEF_VAR(type,name) type name##_##type##_type

DEF_VAR(int, age);  ->  int age_int_type;

 

  • 当宏参数是另一个宏的时候(展开规则)

需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开. 

1. 非'#'和'##'的情况

#define TOW      (2) 
#define MUL(a,b) (a*b) 

printf("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW)); 
这行的宏会被展开为: 
printf("%d*%d=%d\n", (2), (2), ((2)*(2))); 
MUL里的参数TOW会被展开为(2). 

2. 当有'#'或'##'的时候

#define A          (2) 
#define STR(s)     #s 
#define CONS(a,b)  int(a##e##b) 

printf("int max: %s\n",  STR(INT_MAX));    // INT_MAX #include<climits> 
这行会被展开为: 
printf("int max: %s\n", "INT_MAX"); 

printf("%s\n", CONS(A, A));               // compile error  
这一行则是: 
printf("%s\n", int(AeA)); 

INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏. 
加这层宏的用意是把所有宏的参数在这层里全部展开, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数. 

#define A           (2) 
#define _STR(s)     #s 
#define STR(s)      _STR(s)          // 转换宏 
#define _CONS(a,b)  int(a##e##b) 
#define CONS(a,b)   _CONS(a,b)       // 转换宏 

printf("int max: %s\n", STR(INT_MAX));          // INT_MAX,int型的最大值,为一个变量 #include<climits> 
输出为: int max: 0x7fffffff 
STR(INT_MAX) -->  _STR(0x7fffffff) 然后再转换成字符串; 

printf("%d\n", CONS(A, A)); 
输出为:200 
CONS(A, A)  -->  _CONS((2), (2))  --> int((2)e(2)) 

 

  • 重新实现NAME_TO_STR宏

通过上面的说明已经明白了宏展开的规则了,那么之前写的NAME_TO_STR就存在问题了。不能在宏参数中放入有宏的参数。

第一种情况:

#define NAME_TO_STR(name) (_T(#name))

_tprintf(NAME_TO_STR(name_to_string)); // 输出字符串"name_to_string" 

测试没有问题。

第二种情况:

#define USER_NAME zhangsan

#define NAME_TO_STR(name) (_T(#name))

_tprintf(NAME_TO_STR(USER_NAME)); // 输出字符串"USER_NAME",和预期的"zhangsan"不符合

 

所以也要通过增加一层中间转换宏,实现如下:
#define _NAME_TO_STR(name) (_T(#name))
#define NAME_TO_STR(name) (_NAME_TO_STR(name))

#define USER_NAME zhangsan

_tprintf(NAME_TO_STR(USER_NAME)); // 输出字符串"zhangsan"

 

  • 再看_T, TEXT宏的实现

winnt.h中对TEXT的实现如下:

#ifdef UNICODE
#define __TEXT(quote) L##quote
#define TEXT(quote) __TEXT(quote)
#endif

可见和我们之前讲解NAME_TO_STR宏的思路一样,也需要增加一层中间宏,不然也不能传入含有宏的参数

 

  • 关于...的使用

...在C宏中称为Variadic Macro,也就是变参宏。简单看下下面的使用方式吧
 

void MakeLog(const char *pszfile, int nLine, const char *pszFunction,
             const char *fmt, ...) {
  va_list ap;
  const int nMaxLen = 1024;
  va_start(ap, fmt);
  char szData[nMaxLen] = {0};
  _vsnprintf_s(szData, nMaxLen - 1, _TRUNCATE, fmt, ap);
  va_end(ap);

  //LogFile(pszfile, nLine, pszFunction, szData);
}
#define LogFormat(fmt, ...) MakeLog(__FILE__,__LINE__,__FUNCTION__,fmt, ##__VA_ARGS__)

LogFormat("%s %s", "username:", "zhangsan");

 

 

参考文章:https://blog.csdn.net/u011671986/article/details/78912584

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值