之前也看过一些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