当前位置: 首页 > 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.gsyq.cn/news/1599522.html

相关文章:

  • Prometheus/Grafana 监控体系:从指标采集到告警收敛的深度部署
  • 从坐标系到制导律:导弹运动建模中的关键角度与力
  • GraphCast图神经网络如何重构中短期气象预报范式
  • 从单 Agent 到多 Agent:为什么协作难落地
  • 【TEE从入门到精通及实战】74 TEE中的内存安全:从Wasm沙箱到硬件隔离的最后一公里
  • 【学习笔记】RLHF 与 DPO:让模型对齐人类偏好的两条路(8/35)
  • AI Agent 运行时革命:从上下文状态到事件日志范式
  • MCQTSS_QQMusic技术解析:QQ音乐API逆向工程与自动化数据获取解决方案
  • 瑞萨RL78 RFD驱动集成指南:Smart Configurator实现Flash编程
  • Python实现混合加密文件传输:RSA+AES-GCM构建安全通信系统
  • Outfit字体:9种字重免费开源字体库的终极选择
  • 6大网盘高速直链下载:油猴脚本完全配置指南
  • 从零搭建私有CA与Nginx HTTPS配置:SSL证书自制全流程详解
  • 认知函数驱动的AI建模:从人脑机制到可解释智能系统
  • Godot PCK解包工具:三步轻松提取Godot游戏资源
  • RA8T2以太网GWCA寄存器配置:从描述符链到TSN时间戳的实战指南
  • RePKG:Wallpaper Engine资源提取与纹理转换的终极指南
  • 如何通过Typora与Xmind联动,实现笔记到导图的离线一键转换
  • Python自动化工具实战指南:高效处理抖音创作者作品批量采集
  • 终极指南:如何用smcFanControl解决Mac过热降频问题
  • HTTP流量拦截与修改实战:Fiddler和BurpSuite抓包改包指南
  • Video2X:三步实现AI视频画质与流畅度双重提升
  • 【宝塔面板排障】服务启动失败?三步精准定位并修复“Panel服务”卡死难题
  • 运城高口碑黄金铂金回收白银回收实体老店排行 5 家靠谱门店电话地址全收录
  • Play Integrity Checker 终极指南:快速检测Android设备完整性的免费工具
  • 神经网络概念解码:从梯度流到泛化机制的七层穿透
  • 安卓手机管理还在用数据线?这款Windows工具,备份传输一键搞定!
  • AI生成20万字专著不再愁!专业工具推荐,开启专著写作新体验!
  • CK11N成本滚算:BAPI与BDC两种自动化方案的技术选型与实战解析
  • 华为云服务器(2288H V5)硬件扩容实战:从内存插槽规划到存储池配置