网站首页 > 技术文章 正文
目标:掌握 预处理(preprocessing) 的关键能力:字符串化(#)、记号拼接(##)、可变参数宏、do{...}while(0) 惯用法、全括号、无副作用、以及 C11 _Generic 做“类型感知宏”。给你一套可直接抄用的宏模板。
1)常量优先用 enum/static const
enum { SZ_PAGE = 4096 }; // 编译期常量,参与 case/数组维度
static const int MAX_CONN = 1024; // 只读变量,比 #define 更安全
2)表达式宏三件套:全括号 + 无副作用 + do-while(0)
#define SQR(x) ((x) * (x)) // 参数+整体都加括号
// 语句式宏:保证使用时像一条语句
#define PUSH(v) do { stack[top++] = (v); } while (0)
禁忌:在宏参数中产生副作用(如 x++):
#define BAD_MIN(a,b) ((a)<(b)?(a):(b))
int m = BAD_MIN(i++, j++); // 副作用求值两次
替代:用 static inline 函数或 C11 _Generic:
#define MIN(a,b) _Generic((a)+(b), \
long double: fminl, double: fmin, float: fminf, default: NULL)(a,b)
没有合适重载时,直接用 static inline 是首选。
3)字符串化与记号拼接
#define STR(x) #x
#define CAT(a,b) a##b
printf("%s\n", STR(Hello World)); // → "Hello World"
int CAT(user, _id) = 7; // → int user_id = 7;
4)可变参数宏(日志/包装)
#define LOG_INFO(fmt, ...) \
do { fprintf(stderr, "[I] %s:%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); } while(0)
说明:##__VA_ARGS__ 是 GNU 扩展,可消除“空参数”时的多余逗号;要可移植,把逗号放进 fmt 控制。
5)类型安全的“数组元素个数”宏
// 仅在实数组上成立,传指针会编译期报错(C11)
#define COUNT_OF(a) \
(sizeof(a)/sizeof((a)[0]) + 0* _Generic(&(a), const void *: 0, default: 0))
简洁可用的版本:#define COUNT_OF(a) (sizeof(a)/sizeof((a)[0]))(注意:只能用于定义处数组)。
6)宏的作用域与防污染
- 命名建议:全大写 + 前缀(如 UTIL_COUNT_OF)。
- 用完 #undef 清场;头文件使用 include guard 或 #pragma once。
- 只在私有 .c 使用的内部宏不进公共头。
7)宏与内联的取舍
- 可以纯用宏的,多数也能改成 static inline:更安全、可调试、有类型检查。
- 一定要用宏的场景:#if 条件编译、文件名/行号注入、生成标识符等。
8)完整模板库(收藏即用)
// 语句式宏
#define DEFER(stmt) do { stmt; } while(0)
// 字符串化/拼接
#define STR(x) #x
#define CAT(a,b) a##b
// 断言(简版)
#define ASSERT(cond) do { \
if(!(cond)){ fprintf(stderr,"ASSERT %s:%d: %s\n",__FILE__,__LINE__,#cond); abort(); } \
} while(0)
// scope guard(需要 GNU 扩展时更强)
static inline void swap_int(int* a,int* b){ int t=*a;*a=*b;*b=t; }
#define COUNT_OF(a) (sizeof(a)/sizeof((a)[0])) // 仅数组定义处
9)自检清单
- 参数与整体全括号
- 语句式宏用 do{...}while(0)
- 避免副作用;必要时改 static inline
- #/## 熟练使用
- 日志宏带 __FILE__/__LINE__
- include guard/#pragma once 防重复包含
10)小练习
1)把 SWAP(a,b) 写成安全宏或 static inline。
2)写一个 LOG_ERR,当 errno 非 0 时附带错误字符串。
猜你喜欢
- 2025-09-14 在Excel用max()/min()函数代替IF()函数的多分支判断的一个小案例
- 2025-09-14 用数学的方法理解Photoshop混合模式——变暗模式
- 2025-09-14 这4个变态的Excel函数公式,却好用的很
- 2025-09-14 常用函数示例_常用函数示例大全
- 2025-09-14 Excel教程:常用的Excel统计函数汇总
- 2025-09-14 办公小技巧:用好Excel 2019新函数为办公提速
- 2025-09-14 如何实现一个带min函数的栈?这招双栈法让查询速...
- 2025-09-14 python常用得内置函数解析——min()函数
- 2025-07-10 Python 元组(Tuple)详解(python元组用来做什么)
- 2025-07-10 Excel如何去除前导0,中间和末尾的0不去除?送大家一条通用公式
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)