一、什么是宏?
预处理阶段的文本替换机制
宏定义的三种形式
① 对象式宏:简单的常量定义和文本替换
② 函数式宏:接收参数的可复用代码片段
③ 条件编译宏:根据条件编译不同代码
基本语法示例
cpp
#define PI 3.14159
#define MAX(a, b)((a)>(b)?(a):(b))
#define SWAP(x,y) do { /
int temp =x; /
x = y; /
y = temp; /
}while(0)
#ifdef DEBUG
#define DPRINT(x)printf(x)
#else
#define DPRINT(x)
#endif
核心特点:
- 编译前处理:宏在预处理阶段展开,不占用运行时开销
- 文本替换:直接进行字符串替换,无函数调用开销
- 灵活性高:可以生成各种代码变体和优化方案
二、寄存器操作宏
嵌入式开发中最常见的宏类型:寄存器读写宏
cpp
/*读寄存器*/
#define READ_REG(reg) (*(volatile uint32_t*)(reg))
/*写寄存器*/
#define WRITE_REG(reg,val) \
(*(volatile uint32_t*)(reg)=(val))
/*实际使用 */
uint32_t data= READ_REG(GPIO_IDR_ADDR);
WRITE_REG(GPIO_ODR_ADDR, 0x0001);
三、位操作宏
cpp
/*位操作*/
#define SET_BIT(reg, bit) \
( (reg) |= (1<<(bit)) ) //1左移bit位,也就是bit位置1
#define CR_BIT(reg, bit) \
( (reg) &= ~(1 <<(bit)) ) //1左移bit位后取反,也就是bit位置0
#define GET_BIT(reg, bit) \
(( (reg)>>(bit) ) & 1) //取bit位的值,1或0
/*实际使用 */
/*设置第5位*/
SET_BIT(GPIO_PORT, 5);
/*清除第3位 */
CLR_BIT(GPIO_PORT, 3);
/*检査第7位 */
if(GET_BIT(GPIO_PORT, 7)){
// do something
}
关键点:
使用 volatile防止编译器优化,确保每次都读写寄存器。加括号提高安全性。
四、实用工具宏
提高编码效率的常见模式
1、常用计算宏
cpp
/*获取数组元素个数*/
#define ARRAY_SIZE(arr) \
(sizeof(arr) / sizeof((arr)[0]))
/*获取结构体成员偏移量 */
#define OFFSET_OF(type, member) \
((size_t)&((type *)0)->member)
/*容器向后指针获取*/
#define CONTAINER_OF(ptr, type, member) \
((type *)((char *)(ptr) - OFFSET_OF(type, member)))
/*实际使用*/
int array[]={1,2,3,4,5};
int size = ARRAY SIZE(array);/* size = 5 */
2、调试和断言宏
cpp
/*调试打印 */
#define DEBUG_PRINT(fmt, ...) \
printf("[%s:%d]" fmt "\n",\
__FILE__, __LINE__, ##__VA_ARGS__)
/*断言宏 */
#define AsSERT(condition) \
if(!(condition)){ \
printf("Assert failed: %s\n", #condition); \
while(1);\
}
/*实际使用 */
DEBUG_PRINT("Value:%d", 42);
ASSERT(ptr != NULL);
do-while(0):确保宏在任何上下文中都是单一语句
##VA_ARGS :处理可变参数宏,支持灵活参数
#运算符:将参数转换为字符串用于调试
五、宏的优缺点
1、优势详解
- 零运行时开销:编译时展开,无函数调用开销
- 代码膨胀可控:只在需要的地方展开
- 类型灵活性:不受类型限制,支持通用编程区
- 编译时检查:条件编译实现平台特定代码
- 可维护的常量:集中管理配置参数
- 性能关键路径优化:内联展开替代函数调用
典型应用场景
- 硬件寄存器操作、嵌入式系统配置、实时系统的关键路径优化、跨平台代码编译控制。
2、宏的5大劣势
- 调试困难:展开后难以追踪,堆栈信息混乱
- 代码膨胀:过度使用导致可执行文件变大
- 易出错:参数未加括号引发优先级错误
- 难以维护:宏定义散落各处,难以追踪
- 名字冲突:全局命名空间污染,易产生冲突

优先使用 const 替代常量;使用 static inline替代简单宏;宏参数必须加括号;宏名使用大写;集中管理宏定义。
六、常用宏
cpp
寄存器读写宏 - 这是硬件交互的基础
#define READ_REG(reg) (*(volatile uint32_t *)(reg))
#define WRITE_REG(reg, val) (*(volatile uint32_t *)(reg) = (val))
位操作宏 - 单片机控制的杀手锏
#define SET_BIT(reg, bit) ((reg) |= (1 << (bit)))
#define CLR_BIT(reg, bit) ((reg) &= ~(1 << (bit)))
数组长度宏 - 避免手动计算
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
调试打印宏 - 开发效率倍增
#define DEBUG_PRINT(fmt, ...) printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
断言宏 - 及时发现问题
#define ASSERT(condition) if(!(condition)) { printf("Assert failed\n"); while(1); }
条件编译宏 - 跨平台开发必备
#ifdef DEBUG
#define LOG(x) printf(x)
#else
#define LOG(x)
#endif