C使用宏参和构造有规律的字符串常量标识符常量值
预处理符号#:可以将宏参粘合成字符串的一部分。
预处理符号##:可以将宏参粘合成标识符、常量值的一部分。
通常可以用于构造有规律的字符串、标识符、常量值。#include #define MACR1(s) #s #define MACR2(a,b) a##b int main(void) { int helloworld = 101; printf("%s ", MACR1(hello)" world");// hello world,#将宏参粘合成了一个字符串 printf("%d ", MACR2(hello,world)); // 101,##将宏参粘合成了一个标识符 printf("%d ", MACR2(10,1)); // 101,##将宏参粘合成了一个字面值常量 return 0; }
在 Linux 内核中有一个使用# 和## 的真实案例,通过这个例子可以了解到# 和## 的价值所在。
(1)没有#和##时的正常做法 #define QDSP_MODULE_AUDPPTASK 1 #define QDSP_MODULE_AUDRECTASK2 #define QDSP_MODULE_UDPREPROCTASK 3 struct adsp_module_info { const char *name; const char *pdev_name; uint32_t id; }; struct adsp_module_info module_info[] = { {.name="AUDPPTASK", .pdev_name=adsp_AUDPPTASK, .id=QDSP_MODULE_AUDPPTASK}, {.name="AUDRECTASK", .pdev_name=adsp_AUDRECTASK, .id=QDSP_MODULE_AUDRECTASK}, {.name="UDPREPROCTASK", .pdev_name=adsp_UDPREPROCTASK, .id=QDSP_MODULE_UDPREPROCTASK} };
从这个例子中可以看出,给结构体数组初始化的值的名字很有规律,比如: "AUDPPTASK" adsp_AUDPPTASK QDSP_MODULE_AUDPPTASK_1
基于这个规律完全可以使用 # 和## 处理,在Linux 内核中确实也是这么做的。
(2)使用#和##处理后 #define QDSP_MODULE_AUDPPTASK_1 1 #define QDSP_MODULE_AUDRECTASK_2 2 #define QDSP_MODULE_AUDRECTASK_3 3 #define QDSP_MODULE(n) { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n } struct adsp_module_info { const char *name; const char *pdev_name; uint32_t id; }; struct adsp_module_info module_info[] = { QDSP_MODULE(AUDPPTASK), QDSP_MODULE(AUDRECTASK), QDSP_MODULE(UDPREPROCTASK) };
使用 # 和## 修改后,其实代码的执行效率并没有发生变化,但是使用了# 和## 后,确使得源码更加的简洁。
在Linux 内核、框架的C/C++ 源码中,大量充斥着这种用法,希望通过这里的介绍后,大家不再陌生这样的用法。
如果宏的参数是另一个宏的话,通常需要加一层宏定义,才可以产生预想的效果: #include #define NUM 100 #define STR(num) #num #define _STR(num) STR(num) int main(void) { printf("%s ", _STR(NUM)); return 0; }
展开的过程: _STR(NUM) STR(NUM) STR(100) #100 "100"
如果没有加的这一层,NUM就没有展开的机会。
再看##: #include #define TAG1 info #define TAG2 _teacher #define STRUCT(a, b) struct a##b #define _STRUCT(a, b) STRUCT(a, b) //加的一层 _STRUCT(TAG1, TAG2) { int num; } int main(void) { return 0; }
展开的过程: _STRUCT(TAG1, TAG2) STRUCT(TAG1, TAG2) STRUCT(info, _teacher) struct info##_teacher struct info_teacher
如果没有加的这一层,TAG1, TAG2就没有展开的机会。
再看一个##定义结构体的例子: struct info_student { char name[30]; int num; }; typedef struct info_student student;struct info_teacher { char name[30]; int id; }; typedef info_teacher teacher;struct info_administor { char name[30]; int id; }; typedef struct info_administor administor;
比如像以上的这些例子,当需要定义好多结构体类型,而且这些结构体类型的格式还非常相似时(有规律可循),我们可以使用 ## 来简化操作,让代码变的更简洁:
2)使用##简化后 #define STRUCT(type) typedef struct info_##type type; struct info_##type STRUCT(student) { char name[30]; int num; }; STRUCT(teacher) { char name[30]; int id; }; STRUCT(administor) { char name[30]; int id; }; int main(void) { return 0; }
预处理后的代码:typedef struct info_student student; struct info_student { char name[30]; int num; }; typedef struct info_teacher teacher; struct info_teacher { char name[30]; int id; }; typedef struct info_administor administor; struct info_administor { char name[30]; int id; }; int main(void) { return 0; }
ref
https://www.hhcycj.com/post/item/382.html
-End-