C 语言中宏的正常用法

本文详细介绍了C语言中宏的用法,包括对象宏和函数宏的定义及应用,如逻辑运算、数据结构创建、控制逻辑实现。通过实例展示了如何使用宏进行代码优化,如`PP_BOOL`用于数值到布尔转换,`PP_IF`实现条件判断,以及`PP_WHILE`模拟循环。同时提到了预处理器的规则和GCC中查看宏展开的技巧,并推荐了Boost库的preprocessor模块。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本文介绍一些 C/C++ 中宏的正常用法。 以及一些你绝对不会想让你家孩子知道的令人惊叹的技术。

1 相关知识

宏是在编译之前由预处理器处理的替换规则, 仅进行字符串替换, 并没有值的感念. 宏有两种风格, 一种和对象类似:

#define identifier replacement-list

这里 identifier 是宏的名字, replacement-list 是一个或多个 tokens 的序列。在后面的程序中所有出现 identifier 的地方都会被展开为 replacement-list 。
另一种是函数风格的宏,它就好比“预处理期的元函数”:

#define identifier(a1, a2, ... an) replacement-list

这里,每一个ai 都代表一个宏形参(parameter)的名字。 如果后面的程序中用到了该宏,并给出了适当的实参(argument), 那么它将被扩展为 replacement-list ——其中每次出现宏形 参的地方都会被替换为用户给出的宏实参。
如果学习过 C 语言,我们通常会像这样使用宏:

int bigarray[MAXN];

这样在预处理期, MAXN会被替换成10001, 也就是说上面的代码在预处理之后变成了

int bigarray[10001];

使用 gcc的 -E 选项可以查看预处理结果, 也有更好的方式, 将在后面介绍.

函数形式的宏比较常用:

#ifndef min
# define min(x,y) ({
      \
    typeof(x) _x = (x);     \
    typeof(y) _y = (y);     \
    (void) (&_x == &_y);    \
    _x < _y ? _x : _y; })
#endif

#ifndef max
# define max(x,y) ({
      \
    typeof(x) _x = (x);     \
    typeof(y) _y = (y);     \
    (void) (&_x == &_y);    \
    _x > _y ? _x : _y; })
#endif

#ifndef swap
#define swap(x,y) do {
      \
    typeof(x) __tmp = (x); \
    (x) = (y); (y) = __tmp; \
} while(0)
#endif

swap 宏中do … while(0) 的作用是将几条语句合成一条. 如果没有 do {…} while(0), 下面这句话就会有问题:

if (x > y)
    swap(x, y);

如果没有 do {…} while (0), 则只有 typeof(x) __tmp = (x); 在 if 的条件为真下执行, 其它会在任何条件下执行.

在GCC环境中,如果对效率有较高的要求, 可能会需要下面这两个宏:

#ifndef likely
# define likely(x)              __builtin_expect(!!(x), 1)
#endif

#ifndef unlikely
# define unlikely(x)            __builtin_expect(!!(x), 0)
#endif

ikely(x) 告诉编译器 x 为真的概率较大, 编译器据此可以优化跳转预测.

比较常用的宏还有下面这个:

#ifndef container_of
/**
 * container_of - cast a member of a structure out to
 *   the containing structure
 * @ptr:        the pointer to the member.
 * @type:       the type of the container struct this is embedded in.
 * @member:     the name of the member within the struct.
 *
 */
# define container_of(ptr, type, member) ({
                          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

它的用处注释里都已经说清楚了。

2 宏的规则

如果你忘记了, 那么函数形式的宏是这个样子的:

#define identifier(a1, a2, ... an) replacement-list

identifier(…) 替换为 replacement-list, 其中的实參将按照宏的定义放置到对应位置.
如果实參是宏, 则先展开 identifier 的实參, 再展开当前宏定义的 identifier(…), 除非遇到规则3.
replacement-list 中形如 # a1 的被替换为字符串 a1.
replacement-list 中的 name## a1 将被预处理器连接 namea1, 其中 a1 为实參.
replacement-list 中出现当前定义的 identifier, 则停止展开.

3 打印宏展开后的样子

仔细观察宏的规则, 很容易理解将宏展开之后的样子转换为字符串以打印出来的宏:

#define PP_STRINGIZE(text) PP_STRINGIZE_I(text)
#define PP_STRINGIZE_I(text) #text

此后就可以使用这个宏来观察一个宏展开之后的样子了:

puts(PP_STRINGIZE(min(1, 2)));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值