C语言带参数的宏定义的相关知识汇总(最常用的形式、带标记分隔符##的形式...)

发布于:2025-02-11 ⋅ 阅读:(40) ⋅ 点赞:(0)

阅读大型C工程代码时,绕不开带参数的宏定义的阅读,所以有必要强化一下这一块的知识。

01-带参数的宏定义最常用的形式

# define S(a,b) a*b
...
...
...
area = S(3,2);

则在编译预处理时area = S(3,2);被展开为:

area = 3 * 2;

02-带标记分隔符##的宏定义

为什么要引入标记分隔符##

在 C 语言的 预处理器中,对于带参数的宏替换,实际上是以标记为单位来替换的。
比如在下面的宏定义:

# define S(a,b) a*b

a*b中,a是一个标记,b也是一个标记,两个标记通过运算符*进行了划分。
在实际展开时,第一个参数名因为与第一个标记相同,所以去替换第一个标记a;第二个参数名因为与第二个标记相同,所以去替换第二个标记b。

又如:

# define S(aaa,bbb) aaa*bbb

aaa*bbb中,aaa是一个标记,bbb也是一个标记,两个标记通过运算符*进行了划分。
在实际展开时,第一个参数名因为与第一个标记相同,所以去替换第一个标记aaa;第二个参数名因为与第二个标记相同,所以去替换第二个标记bbb。
此时

S(su,wenhao)

会展开为:su*wenhao

但是对于下面这个宏定义:

#define S1(aaa, bbb) aaa*bbbk

就出问题了,因为在aaa*bbbk中,两个标记分别为aaa和bbbk(两个标记通过运算符*进行了划分),第一个参数名因为与第一个标记相同,所以去替换第一个标记aaa,第二个参数名因与第二个标记bbbk不相同,所以无法替换,此时:

S(su,wenhao)

的展开结果为:su*bbbk
但其实我们这里想得到的结开结果为su*wenhaok

那么办呢?我们就需要在bbbk之间加一个标记分隔符,这个标记分隔符就是##,即把宏定义写成下面这样就能得到我们想要的结果:

#define S1(aaa, bbb) aaa*bbb##k

## 的用途举例

1. 动态生成变量名

通过宏创建多个变量名:

#define CREATE_VAR(name) int var_##name;
CREATE_VAR(foo)  // 生成 int var_foo;
CREATE_VAR(bar)  // 生成 int var_bar;
2. 动态生成函数

可以用宏来定义多个函数:

#define CREATE_FUNC(name) void func_##name(void) { printf(#name " function called\n"); }
CREATE_FUNC(init)  // 生成 void func_init(void) { printf("init function called\n"); }
CREATE_FUNC(exit)  // 生成 void func_exit(void) { printf("exit function called\n"); }

调用时:

func_init();  // 输出:init function called
func_exit();  // 输出:exit function called

我在实际工程中遇到使用##的例子

在Linux嵌入式驱动开发中,对于设备类的属性的定义用到了带##的宏。

CLASS_ATTR_WO(led_all_control);

这个宏的定义如下:

#define CLASS_ATTR_WO(_name) struct class_attribute class_attr_##_name = __ATTR_WO(_name)

由于分隔符##的存在,所以_name成为一个标记,所以CLASS_ATTR_WO(led_all_control);会被展开为:

struct class_attribute class_attr_led_all_control = __ATTR_WO(led_all_control)

上面这个展开式的右边其实又是一个带参数的宏定义,这里我就不把它展开了,我们这里重点是强调宏标记分隔符##的使用。


网站公告

今日签到

点亮在社区的每一天
去签到