当前位置: 首页 > news >正文

第28篇 预处理详解

目录

一、预定义符号与#define常量定义

1. 预定义符号

2. #define定义常量

⚙️ 二、宏定义机制与副作用

1. 宏定义语法

2. 宏参数副作用

3. 宏替换规则

4. 宏与函数对比

5. #和##运算符

🏷️ 三、命名约定与#undef指令

1. 命名约定

2. #undef指令

🛠️ 四、命令行定义与条件编译

1. 命令行定义

2. 条件编译

📂 五、头文件包含与防重复包含

1. 包含方式

2. 嵌套包含问题

📜 六、其他预处理指令


一、预定义符号与#define常量定义

1. 预定义符号

C语言内置了若干预定义符号,在预处理阶段直接处理,无需定义即可使用:

  • __FILE__:当前编译的源文件名
  • __LINE__:当前行号
  • __DATE__:文件编译日期
  • __TIME__:文件编译时间
  • __STDC__:若编译器遵循ANSI C标准,值为1,否则未定义
2. #define定义常量
  • 基本语法#define name stuff
  • 示例
    • #define MAX 1000:定义数值常量
    • #define reg register:为关键字创建简短别名
    • #define do_forever for(;;):用形象符号替换实现
    • #define DEBUG_PRINT printf("file:%s\tline:%d\tdate:%s\ttime:%s\n", __FILE__, __LINE__, __DATE__, __TIME__):多行定义需使用反斜杠\续行
  • 注意事项:定义标识符时不建议在末尾加分号,否则可能导致语法错误。例如:
    #define MAX 1000; // 错误示例 if (condition) max = MAX; // 替换后变成max = 1000;;,导致if和else之间出现两条语句 else max = 0;

二、宏定义机制与副作用

1. 宏定义语法
  • 声明方式#define name(parament-list) stuff
  • 注意:参数列表的左括号必须与宏名紧邻,否则会被解释为stuff的一部分。
  • 示例#define SQUARE(x) x * x
  • 陷阱与修复
    • 问题1SQUARE(a + 1)替换后变成a + 1 * a + 1,运算顺序错误。
      • 修复#define SQUARE(x) (x) * (x)
    • 问题2#define DOUBLE(x) (x) + (x),在10 * DOUBLE(a)中替换为10 * (a) + (a),乘法优先级导致错误。
      • 修复#define DOUBLE(x) ((x) + (x))
  • 原则:数值表达式宏定义应在参数和整体表达式两边都加括号,避免运算符优先级问题。
2. 宏参数副作用
  • 副作用定义:表达式求值时产生的永久性效果(如x++)。
  • 危险示例
    #define MAX(a, b) ((a) > (b) ? (a) : (b)) z = MAX(x++, y++); // 替换后:((x++) > (y++) ? (x++) : (y++)) // 结果:x和y可能被多次自增,导致不可预测结果
3. 宏替换规则
  1. 调用宏时,先检查参数是否包含#define定义的符号,若有则先替换。
  2. 替换文本插入原位置,宏参数被值替换。
  3. 再次扫描结果文件,重复上述过程。
  • 注意:宏参数和定义中可包含其他宏,但宏不能递归;字符串常量内容不被搜索。
4. 宏与函数对比
属性#define宏函数
代码长度每次使用都插入代码,程序长度增长代码只出现一次,调用同一份代码
执行速度更快(无调用开销)有调用和返回开销,稍慢
操作符优先级需加括号避免优先级问题参数求值一次,结果可预测
副作用参数多次计算可能导致不可预料结果参数求值一次,结果可控
参数类型类型无关,适用于任何合法类型参数类型相关,需声明特定类型
调试不方便调试可逐语句调试
递归不能递归可递归
  • 宏的特殊能力:参数可出现类型(如#define MALLOC(num, type) (type*)malloc(num * sizeof(type))),函数无法做到。
5. #和##运算符
  • #运算符:将宏参数转换为字符串字面量(字符串化)。
    #define PRINT(n) printf("the value of "#n" is %d", n) PRINT(a); // 替换为printf("the value of " "a" " is %d", a)
  • ##运算符:连接两边符号,创建新标识符(记号粘合)。
    #define GENERIC_MAX(type) type type##_max(type x, type y) { return (x>y?x:y); } GENERIC_MAX(int) // 生成int int_max(int x, int y) { ... }

三、命名约定与#undef指令

1. 命名约定
  • 宏名:全部大写(如MAX_SIZE
  • 函数名:不全大写(如getMax
2. #undef指令
  • 作用:移除已定义的宏。
  • 语法#undef NAME
  • 用途:重新定义宏前需先移除旧定义。

四、命令行定义与条件编译

1. 命令行定义
  • 功能:编译时在命令行定义符号,用于生成程序不同版本。
  • 示例gcc -D ARRAY_SIZE=10 program.c(Linux环境)
2. 条件编译
  • 用途:选择性编译代码(如调试代码)。
  • 常见指令
    1. #if 常量表达式 ... #endif
    2. #if ... #elif ... #else ... #endif(多分支)
    3. #ifdef symbol/#ifndef symbol(判断是否定义)
    4. 嵌套指令
      #if defined(OS_UNIX) #ifdef OPTION1 unix_version_option1(); #endif #ifdef OPTION2 unix_version_option2(); #endif #elif defined(OS_MSDOS) #ifdef OPTION2 msdos_version_option2(); #endif #endif

五、头文件包含与防重复包含

1. 包含方式
  • 本地文件包含#include "filename"
    • 查找策略:先在源文件所在目录查找,未找到再去标准路径。
  • 库文件包含#include <filename.h>
    • 查找策略:直接去标准路径查找。
  • 标准路径
    • Linux:/usr/include
    • VS:C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
2. 嵌套包含问题
  • 问题:头文件被重复包含会导致内容被多次拷贝,增加编译压力。
  • 解决方案
    1. 条件编译防重
      #ifndef __TEST_H__ #define __TEST_H__ // 头文件内容 #endif
    2. #pragma once:避免重复引入(推荐)。

六、其他预处理指令

  • #error:生成错误信息
  • #pragma:设置编译器状态(如#pragma pack()用于结构体对齐)
  • #line:修改行号信息
http://www.jsqmd.com/news/1087574/

相关文章:

  • 单视频多样性生成技术原理与可行性分析
  • 微信小程序审核失败:AppSecret泄漏风险排查与安全架构重构指南
  • GraphCast图神经网络如何重构中短期气象预报范式
  • 大模型部署架构:从推理引擎到弹性扩缩容的工程实践
  • 从坐标系到制导律:导弹运动建模中的关键角度与力
  • Prometheus/Grafana 监控体系:从指标采集到告警收敛的深度部署
  • 终极兼容方案:ViGEmBus虚拟手柄驱动完全指南
  • Codex EAI_AGAIN DNS 临时失败处理教程
  • 【TEE从入门到精通及实战】74 TEE中的内存安全:从Wasm沙箱到硬件隔离的最后一公里
  • 从单 Agent 到多 Agent:为什么协作难落地
  • Hutool RSA加密填充模式详解:跨系统对接避坑指南
  • d2s-editor:暗黑破坏神2存档编辑器的5个核心功能深度解析
  • 如何用misakaX实现iOS深度定制?从入门到精通的完整指南
  • LeagueAkari终极指南:英雄联盟智能辅助工具快速上手秘籍
  • 【学习笔记】RLHF 与 DPO:让模型对齐人类偏好的两条路(8/35)
  • GEO源码部署服务可以代理接单吗
  • SuperDuperDB测试覆盖率实战:从数据层到AI模型的全链路质量保障指南
  • 瑞萨RA MCU USBHS中断与FIFO管理实战指南
  • 统信 UOS 桌面版 OpenClaw 完整部署教程:适配国产系统,实现办公自动化全功能落地
  • 为什么你的软考退税总不通过?资深税务师亲授“3秒识别材料致命缺陷”法(含OCR识别盲区图解)
  • 从SOP到Warranty:解码汽车量产后的关键阶段与质量守护
  • WarcraftHelper:3步解决魔兽争霸3现代兼容性问题的完整指南
  • BiRefNet:高分辨率双边参考图像分割技术革新
  • 瑞萨RL78 RFD驱动集成指南:Smart Configurator实现Flash编程
  • MCQTSS_QQMusic技术解析:QQ音乐API逆向工程与自动化数据获取解决方案
  • AI Agent 运行时革命:从上下文状态到事件日志范式
  • B站视频下载器:解锁大会员4K高清与充电专属内容的最佳解决方案
  • LeagueAkari:智能游戏助手终极配置指南
  • Python实现混合加密文件传输:RSA+AES-GCM构建安全通信系统
  • 终极指南:如何使用Scarab模组管理器轻松安装《空洞骑士》模组