宏定义#define
一、基本概念#define是预处理指令在编译前由预处理器处理负责文本替换、条件编译等不经过编译器语法检查不分配内存也不属于 C 的类型系统。简单来说它就像一个“高级查找替换”工具。编译器正式编译前预处理器会扫描代码将宏名原封不动地替换为对应的替换文本。编译流程源代码 → 预处理 → 编译 → 汇编 → 链接 → 可执行文件预处理器的工作特点特性说明纯文本替换不理解C语法只做字符串替换顺序扫描从上到下遇到宏就展开递归展开展开后的代码可能包含其他宏继续展开不做计算不执行任何运算只做文本处理1.1引出案例//自定义异常处理 #define DSSJ_RE1_CALL(RE_CODE)\ do{\ switch (RE_CODE)\ {\ case DhSysSetJson::DhSysSetIO::UnErr: break;\ case DhSysSetJson::DhSysSetIO::ReadErr: {hWarning 读取配置文件错误,文件不存在或打不开ln; return ;}\ case DhSysSetJson::DhSysSetIO::WriteErr: {hWarning 写入错误,文件路径错误或被占用或文件只读 ln;return;}\ case DhSysSetJson::DhSysSetIO::PaseJsonErr: {hWarning 解析错误,非JSON格式文本 ln;return;}\ case DhSysSetJson::DhSysSetIO::ParseKeyErr: {hWarning 解析错误,JSON字符串和对应的类变量格式对不上,找不到对应的键 ln;return;}\ case DhSysSetJson::DhSysSetIO::ParseKeyTypeErr: {hWarning 解析错误JSON字符串和对应的类变量格式对不上,键类型对不上 ln;return;}\ case DhSysSetJson::DhSysSetIO::AcceptErr:{hWarning 序列化错误,类变量格式和指定的JSON格式对不上 ln; return;}\ default: {hWarning 未知错误 ln; return;}\ }\ }while(false)1.2案例分析\是行续接符用于将多行代码连接成一行逻辑语句。1.2.1作用C/C 预处理器会将\结尾的多行视为一行完整的宏定义。1.2.2原因这是由C/C预处理器的设计特性决定的1. 预处理器的工作方式预处理器按换行符识别每条指令的结束遇到换行就认为当前指令结束\的作用是取消换行符的结束含义告诉预处理器下一行是本行的继续2. 历史原因C语言诞生于1970年代当时的编程环境如穿孔卡片、终端按行处理文本为了兼容这种行模式需要一种机制表示逻辑行跨越多个物理行3. 实际效果对比// 预处理器视角 #define A 1 // 第1行结束宏A定义为1 #define B 2 // 第2行开始宏B定义为2 #define C 1 \ // 第1行未结束继续读第2行 2 // 预处理器合并为 1 2这不是有效语句4. 关键约束续行符只能在预处理器指令内使用不能跨越多个预处理器指令不能续接注释或字符串外的部分本质上这是一个兼容性设计让旧的单行文本处理方式能支持多行宏定义。二、对象宏定义常量或文本片段#define PI 3.14159 #define VERSION 1.0.0 #define MAX_SIZE 1024展开方式直接文本替换int r 5; double area PI * r * r; // 展开为: 3.14159 * 5 * 5三、函数式宏Function-like Macro3.1 基本形式#define SQUARE(x) ((x) * (x))#define MAX(a, b) ((a) (b) ? (a) : (b)) //多参数#define WARNING() do { printf(Warning!\n); } while(0) //无参数3.2宏函数与其他函数比较特性宏函数内联函数普通函数类型检查❌ 无✅ 有✅ 有调试友好❌ 难✅ 好✅ 好性能✅ 最快✅ 快需调用开销参数类型任意固定固定作用域全局受限受限递归不支持可内联递归支持泛型✅❌✅适用范围简单表达式简单逻辑复杂泛型展开时机预处理编译编译3.3宏函数优势优势1零调用开销// 宏函数 #define MAX(a, b) ((a) (b) ? (a) : (b)) int x MAX(a, b); // 展开后直接计算无函数调用// 内联函数 inline int max(int a, int b) { return a b ? a : b; } // 问题类型固定只能处理int // 如果传入其他类型需要重载或模板优势2类型无关泛型能力// 宏函数任何类型都适用 #define MAX(a, b) ((a) (b) ? (a) : (b)) int i MAX(1, 2); double d MAX(1.5, 2.5); char c MAX(a, b);// 函数需要为每种类型写一个重载或模板 inline int max(int a, int b) { return a b ? a : b; } inline double max(double a, double b) { return a b ? a : b; } // ... 需要写很多重载优势3不会有参数求值副作用问题正确写法时// 宏函数 #define MAX(a, b) ({ \ __typeof__(a) _a (a); \ __typeof__(b) _b (b); \ _a _b ? _a : _b; \ }) int x 5, y 10; int m MAX(x, y); // x, y 只求值一次优势4在编译前就可以决定行为#define DEBUG_MODE 1 #if DEBUG_MODE #define LOG(x) cout x endl #else #define LOG(x) // 空操作 #endif // 编译前LOG完全不存在编译器看不到它3.4宏函数劣势劣势1无类型检查风险高#define SQUARE(x) ((x) * (x)) int a SQUARE(hello); // ❌ 编译器不报错预处理却可能出问题劣势2调试困难#define SQUARE(x) ((x) * (x)) int a SQUARE(5 3); // 调试时看到的是展开后的代码不是宏 // 断点打在宏上实际断在展开后的位置劣势3可能产生重复副作用#define SQUARE(x) ((x) * (x)) int a 1; int b SQUARE(a); // ❌ a被自增两次 // 展开为: ((a) * (a))3.5实际选择建议// ✅ 用宏函数的情况 // 1. 简单的类型无关操作 #define MAX(a, b) ((a) (b) ? (a) : (b)) #define SWAP(a, b) do { typeof(a) _t (a); (a) (b); (b) _t; } while(0) // 2. 编译时条件代码 #define UNUSED(x) ((void)(x)) // 3. 必须在预处理阶段决策的逻辑 #ifdef DEBUG #define ASSERT(x) if(!(x)) abort() #else #define ASSERT(x) #endif // ✅ 用内联函数的情况 // 1. 需要类型安全 inline int max_int(int a, int b) { return a b ? a : b; } // 2. 复杂逻辑需要调试 inline double calculate(double x) { double result x * x 2 * x 1; return result; // 可以打断点调试 } // ✅ 用模板函数的情况 // 1. 需要类型安全 泛型 templatetypename T T max_t(T a, T b) { return a b ? a : b; } // ✅ 用成员函数的情况 // 1. 与对象状态相关 class Buffer { public: void clear() { memset(data_, 0, size_); } };四、字符串化操作符#将宏参数转换为字符串#define STRINGIFY(x) #x int a 10; printf(STRINGIFY(a)); // 输出: a printf(STRINGIFY(123)); // 输出: 123原理STRINGIFY(a) → #a → a STRINGIFY(11) → 11 // 注意不会计算结果五、连接操作符##连接两个标记Token#define CONCAT(a, b) a ## b int xy 100; printf(%d\n, CONCAT(x, y)); // 输出: 100 // x ## y → xy → 变量xy应用场景生成变量名、函数名#define GENERATE_VAR(prefix, num) prefix ## num GENERATE_VAR(data, 1); // 生成 data1 GENERATE_VAR(data, 2); // 生成 data2六、可变参数宏...支持任意数量的参数#define LOG(fmt, ...) printf([LOG] fmt \n, __VA_ARGS__) LOG(Value is %d, 42); // [LOG] Value is 42 LOG(Hello); // [LOG] Hello命名可变参数#define LOG(fmt, args...) printf([LOG] fmt \n, ##args)七、条件编译7.1 基本结构#define DEBUG 1 #if DEBUG 1 printf(Debug mode\n); #else printf(Release mode\n); #endif7.2 防止重复包含Include Guard// file.h #ifndef _FILE_H_ #define _FILE_H_ // 头文件内容 #endif // _FILE_H_7.3defined操作符#if defined(DEBUG) DEBUG 0 // 调试代码 #endif八、宏运算符defined#define FEATURE_X 1 #if defined(FEATURE_X) // 等价于 #ifdef FEATURE_X // 启用特性X #endif九、#error 和 #pragma// 编译时错误提示 #if __STDC_VERSION__ 201112L #error C11 or later required #endif // 编译器特定指令 #pragma once // 只包含一次部分编译器支持 #pragma pack(1) // 字节对齐十、高级用法10.1 do-while(0) 技巧// 确保宏像单条语句一样使用 #define SAFE_FREE(p) do { free(p); p NULL; } while(0) // 使用 if (ptr ! NULL) SAFE_FREE(ptr); // 正确只执行一条语句 else printf(NULL pointer);do-while(false)的核心作用解决分号;的歧义问题。问题演示如下1.假设没有do-while(false)#define SAFE_FREE(p) free(p); p NULL;场景1单独使用 ✅SAFE_FREE(ptr); // 展开为: free(ptr); p NULL;;看起来没问题但多了一个分号。场景2用在 if 语句中 ❌ 严重Bugif (ptr ! NULL) SAFE_FREE(ptr); // 用户加了分号 else printf(Already NULL);展开后变成if (ptr ! NULL) free(ptr); p NULL;; // 分号断开了if语句! else // else没有对应的if报错 printf(Already NULL);2.加上 do-while(false) 后#define SAFE_FREE(p) do { free(p); p NULL; } while(0)场景1单独使用 ✅SAFE_FREE(ptr); // 展开为: do { free(ptr); p NULL; } while(0);完美执行分号加在整个语句后面。场景2用在 if 语句中 ✅if (ptr ! NULL) SAFE_FREE(ptr); // 用户加了分号 else printf(Already NULL);展开后变成if (ptr ! NULL) do { free(ptr); p NULL; } while(0); // 整体是一个语句 else printf(Already NULL);else正确绑定到if代码正常工作。图解对比不用 do-while: if (cond) macro; ← ; 让if结束 else ← else孤立语法错误 ... 用 do-while: if (cond) do { ... } while(0); ← ; 加在整个循环后面 else ← else正确绑定 ...为什么不用{}直接包起来#define SAFE_FREE(p) { free(p); p NULL; }if (ptr ! NULL) SAFE_FREE(ptr); // 用户加分号 else printf(...);展开后if (ptr ! NULL) { free(ptr); p NULL; }; // ; 导致if结束 else // else孤立同样有问3.总结方案单独使用if中使用if-else中使用不用包裹✅❌❌用{}✅❌❌do-while(0)✅✅✅do-while(false) 是唯一能完美解决分号问题的结构整体是一个语句可以加分号保证代码块只执行一次兼容所有 C 语法场景10.2 宏代替小函数内联优化。#define IS_EVEN(x) (((x) % 2) 0) if (IS_EVEN(10)) // 比函数调用更快 printf(Even);10.3 类型无关宏#define MAX(type) type max_##type(type a, type b) { \ return a b ? a : b; \ } MAX(int) // 生成 max_int(int a, int b) MAX(float) // 生成 max_float(float a, float b)十一、常见陷阱与最佳实践11.1 括号的重要性// 错误写法 #define SQUARE(x) x * x SQUARE(31); // 展开为 31*31 7 (错误!) // 正确写法 #define SQUARE(x) ((x) * (x)) SQUARE(31); // 展开为 ((31) * (31)) 1611.2 参数重复求值#define MAX(a, b) ((a) (b) ? (a) : (b)) int x 5, y 10; MAX(x, y); // 展开后 x和y可能被多次自增11.3 副作用#define ABS(x) ((x) 0 ? -(x) : (x)) int a -5; ABS(a); // 危险a可能被自增多次十二、完整示例// debug.h #ifndef _DEBUG_H_ #define _DEBUG_H_ #define VERSION 2.0 // 日志宏 #define LOG_INFO(fmt, ...) printf([INFO] fmt \n, ##__VA_ARGS__) #define LOG_ERROR(fmt, ...) printf([ERROR] fmt \n, ##__VA_ARGS__) // 调试开关 #ifdef DEBUG_MODE #define DEBUG_LOG(fmt, ...) printf([DEBUG] fmt \n, ##__VA_ARGS__) #else #define DEBUG_LOG(fmt, ...) #endif // 通用工具宏 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #define MIN(a, b) ((a) (b) ? (a) : (b)) #define MAX(a, b) ((a) (b) ? (a) : (b)) // 禁用拷贝构造和赋值 #define DISABLE_COPY(Class) \ Class(const Class) delete; \ Class operator(const Class) delete; #endif十三、总结对比类型语法用途常量宏#define X value定义常量函数宏#define F(x) expr内联代码模板字符串化#define S(x) #x转参数为字符串连接符#define C(a,b) a##b连接标记可变参#define V(...) expr可变参数支持条件编译#if/#ifdef编译时选择#define的本质是纯文本替换理解这一点就能掌握其所有用法。

相关新闻

Scrapling 实战指南:从单次请求到完整爬虫框架

Scrapling 实战指南:从单次请求到完整爬虫框架

Scrapling 实战指南:从单次请求到完整爬虫框架 【免费下载链接】Scrapling 🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Dont be shy, join here: https://discord.gg/EMgGbDceNQ a…

2026/10/13 2:45:34 阅读更多 →
用架构决策记录(ADR)为项目选定 Python 主力语言:完整示例解析与实战写法

用架构决策记录(ADR)为项目选定 Python 主力语言:完整示例解析与实战写法

【免费下载链接】architecture-decision-record Architecture decision record (ADR) examples for software planning, IT leadership, and template documentation 项目地址: https://gitcode.com/gh_mirrors/ar/architecture-decision-record 点击查看 免费下载 …

2026/10/13 2:45:34 阅读更多 →
从4.5到5.5:长上下文与多步骤任务能力跃迁及成本优化实践

从4.5到5.5:长上下文与多步骤任务能力跃迁及成本优化实践

1. 这次版本跨度到底改了什么1.1 从命名看定位:小版本号,大动作第一次看到“4.5 到 5.5”这个跨度,我下意识以为又是一次常规迭代——版本号加个 1,参数微调一下,跑分涨几个点,然后发篇公告完事。但实际把两…

2026/10/13 2:44:34 阅读更多 →

最新新闻

AIGC创作训练营:从工具使用到工作流设计的系统化训练

AIGC创作训练营:从工具使用到工作流设计的系统化训练

1. 从"会用AI"到"驾驭AI":AIGC创作训练营到底在训练什么很多人第一次接触AIGC,都是从"帮我写一段文案""帮我生成一张图"开始的。提示词丢进去,结果出来,能用就复制粘贴,不能用…

2026/10/13 9:23:55 阅读更多 →
PS5第三方手柄兼容方案:用MCU做HID协议转换层

PS5第三方手柄兼容方案:用MCU做HID协议转换层

前阵子我发了条状态,说自己正在折腾一个叫AnyPS5的小项目,结果评论区一下子涌进来一堆私信,全是问同一个问题:能不能让我手里那些非官方的老手柄在PS5上正常用。说实话,这确实是这个项目最核心的诉求。PS5主机对第三方…

2026/10/13 9:23:55 阅读更多 →
devtron 仓库内 AWS SDK for Go v2 EventStream 协议包全解析:CHANGELOG 版本演进与编解码实现

devtron 仓库内 AWS SDK for Go v2 EventStream 协议包全解析:CHANGELOG 版本演进与编解码实现

云原生DevOps运维 【免费下载链接】devtron The only Kubernetes dashboard you need 项目地址: https://gitcode.com/gh_mirrors/dev/devtron 点击查看 免费下载 导读 本文以 devtron 仓库中锁定的第三方依赖 github.com/aws/aws-sdk-go-v2/aws/protocol/eventst…

2026/10/13 9:23:55 阅读更多 →
bilibili-api完全指南:从零开始掌握B站API开发与应用

bilibili-api完全指南:从零开始掌握B站API开发与应用

bilibili-api完全指南:从零开始掌握B站API开发与应用 B站作为国内领先的视频弹幕网站,拥有丰富的内容生态和开放的API接口,为开发者提供了广阔的创新空间。本指南将带你全面了解B站API的开发与应用,从基础概念到实际操作&#xf…

2026/10/13 9:23:55 阅读更多 →
自助点餐机24小时营业无人值守落地指南丨蓝速科技(larxu)

自助点餐机24小时营业无人值守落地指南丨蓝速科技(larxu)

很多餐饮老板都有过这样的纠结:看着深夜街头偶尔走过的行人,想着要是能多做几单生意该多好,但一算账又退缩了。招一个夜班收银员,工资要比白班高,还得担心人家干不久就离职,流动性大得让人头疼。更现实的是…

2026/10/13 9:23:55 阅读更多 →
Python大数据分析实验通关指南:从数据清洗到可视化全流程

Python大数据分析实验通关指南:从数据清洗到可视化全流程

1. 从lab7这个编号说起:为什么Python课设总在第七周卡住 如果你正在翻这门课的实验安排,看到lab7这个编号,大概率已经过了前六周的语法基础、循环判断、函数封装,开始进入真正要“处理数据”的阶段了。这门课的名字叫《Python程序…

2026/10/13 9:22:51 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →