【C语言】认识预处理
文章目录一、预定义符号二、#define定义常量用法示例注意事项三、#define定义宏关键注意点四、带有副作用的宏参数五、宏替换的规则六、宏与函数的对比七、#和##运算符7.1 # 运算符字符串化7.2 ##运算符记号粘合八、命名约定区分宏与函数九、#undef移除宏定义十、命令行定义编译时动态配置十一、条件编译常见条件编译指令应用示例调试代码控制十二、头文件的包含12.1 包含方式及查找策略12.2 避免头文件重复包含十三、其他预处理指令一、预定义符号C语言为我们内置了一些预定义符号它们在预处理期间就会被处理我们可以直接在代码中使用无需额外定义。这些符号能为我们提供很多有用的信息__FILE__进行编译的源文件的文件名__LINE__当前代码在文件中的行号__DATE__文件被编译的日期格式为“Mmm dd yyyy”__TIME__文件被编译的时间格式为“hh:mm:ss”__STDC__如果编译器遵循ANSI C标准其值为1否则未定义这些符号在调试代码时非常有用例如printf(Error in file: %s at line: %d\n,__FILE__,__LINE__);当程序运行到这里时会自动打印出错误所在的文件名和行号帮助我们快速定位问题。二、#define定义常量#define最基本的用法是定义常量基本语法为#definenamestuff用法示例定义数值常量#define MAX 1000为关键字创建简短别名#define reg register替换复杂实现为更形象的符号#define do_forever for(;;)简化代码编写#define CASE break;case在switch语句中自动添加break注意事项不要随意添加分号例如#define MAX 1000;这样的定义是不推荐的。当在if(condition)maxMAX;elsemax0;中使用时替换后会变成if(condition)max1000;;elsemax0;导致if和else之间出现两条语句引发语法错误。长内容的分行处理如果定义的内容过长可以分成多行书写除最后一行外每行末尾都加上反斜杠续行符。反斜杠后面不能有任何内容#defineDEBUG_PRINTprintf(file:%s\tline:%d\t \ date:%s\ttime:%s\n,\__FILE__,__LINE__,\__DATE__,__TIME__)三、#define定义宏#define还允许我们定义带参数的宏macro实现更灵活的文本替换。宏的声明方式为#definename(parameter-list)stuff其中parameter-list是由逗号分隔的参数列表它们会出现在stuff中。关键注意点参数列表与名称紧连参数列表的左括号必须与name紧邻中间不能有空白否则参数列表会被解释为stuff的一部分。运算符优先级问题例如#define SQUARE(x) x * x这个宏当传入SQUARE(a 1)时会被替换为a 1 * a 1结果为a a 1而非预期的(a1)^2。解决方法是给参数和整体加上括号#define SQUARE(x) (x) * (x)。更复杂的情况#define DOUBLE(x) (x) (x)当调用10 * DOUBLE(5)时会被替换为10 * (5) (5)结果为55而非100。正确的定义应为#define DOUBLE(x) ((x) (x))。结论用于数值表达式求值的宏应给参数和整体都加上括号避免运算符优先级导致的意外结果。四、带有副作用的宏参数当宏参数在宏定义中出现多次且参数带有副作用时可能会产生不可预测的结果。副作用指表达式求值时产生的永久性效果如x会改变x的值而x1则不会。例如#defineMAX(a,b)((a)(b)?(a):(b))intx5,y8,z;zMAX(x,y);预处理后会变成z((x)(y)?(x):(y));执行后x的值变为6y变为10z变为9与直观预期可能不符。这就是副作用参数带来的问题。五、宏替换的规则在扩展#define定义的符号和宏时预处理器遵循以下步骤调用宏时首先检查参数是否包含#define定义的符号若有则先替换。将替换文本插入到原位置宏的参数名被其值替换。再次扫描结果文件若包含#define定义的符号重复上述过程。注意宏参数中可以包含其他#define定义的符号但宏不能递归。预处理器不会搜索字符串常量的内容即字符串中的符号不会被替换。六、宏与函数的对比宏和函数都能实现代码复用但它们在多个方面存在差异属性#define定义宏函数代码长度每次使用都会插入代码可能大幅增加程序长度除非宏很短代码只出现一次每次调用都使用同一份代码执行速度更快无函数调用和返回开销较慢存在函数调用和返回的额外开销操作符优先级可能受周围表达式优先级影响需谨慎使用括号参数只在传参时求值结果传递给函数表达式求值可预测副作用参数参数若多次出现副作用可能导致不可预料的结果参数只在传参时求值一次副作用易控制参数类型与类型无关只要操作合法即可用于任何类型与类型相关不同类型可能需要不同函数即使功能相同调试不方便调试预处理阶段已替换可逐语句调试递归不能递归可以递归宏的独特优势宏可以接受类型作为参数而函数无法做到。例如#defineMALLOC(num,type)(type*)malloc(num*sizeof(type))// 使用int*pMALLOC(10,int);// 替换后(int *)malloc(10 * sizeof(int));七、#和##运算符7.1 # 运算符字符串化#运算符能将宏的参数转换为字符串字面量仅用于带参数的宏的替换列表中。例如#definePRINT(n)printf(the value of #n is %d,n);// 调用inta10;PRINT(a);// 替换后printf(the value of a is %d, a);输出结果为the value of a is 107.2 ##运算符记号粘合##可以将两边的符号合并为一个符号允许从分离的文本片段创建标识符称为“记号粘合”。为不同类型定义求最大值的函数#defineGENERIC_MAX(type)\type type##_max(type a,type b)\{\returnab?a:b;\}GENERIC_MAX(int);//int_max()GENERIC_MAX(float);//float_max()intmain(){intr1int_max(3,9);floatr2float_max(4.6,8.9);printf(%d %f,r1,r2);return0;}八、命名约定区分宏与函数宏和函数的使用语法相似为了区分二者通常遵循以下约定宏名全部大写如MAX、SQUARE函数名不要全部大写如int_max、add九、#undef移除宏定义#undef指令用于移除已有的宏定义语法为#undefNAME如果需要重新定义一个已存在的宏应先使用#undef移除其旧定义。十、命令行定义编译时动态配置许多C编译器允许在命令行中定义符号用于启动编译过程。这在根据同一源文件编译程序的不同版本时非常有用。根据内存大小动态调整数组长度// 源文件program.c#includestdio.hintmain(){intarray[ARRAY_SIZE];for(inti0;iARRAY_SIZE;i){array[i]i;printf(%d ,array[i]);}return0;}编译时在命令行定义ARRAY_SIZE# Linux环境gcc-DARRAY_SIZE10program.c# 定义数组长度为10十一、条件编译条件编译指令允许我们选择性地编译或放弃某些语句常用于调试代码的开关控制。常见条件编译指令基本形式#if常量表达式// 代码段#endif预处理器会求值常量表达式为真则编译代码段。多分支形式#if常量表达式// 代码段1#elif常量表达式// 代码段2#else// 代码段3#endif判断符号是否定义只判断是否被定义不判断真假#ifdefined(symbol)// 等价于 #ifdef symbol// 代码段#endif#if!defined(symbol)// 等价于 #ifndef symbol// 代码段#endif嵌套指令#ifdefined(OS_UNIX)#ifdefOPTION1unix_version_option1();#endif#elifdefined(OS_MSDOS)#ifdefOPTION2msdos_version_option2();#endif#endif应用示例调试代码控制#define__DEBUG__1// 定义调试符号intmain(){intarr[10]{0};for(inti0;i10;i){arr[i]i;#ifdef__DEBUG__printf(arr[%d] %d\n,i,arr[i]);// 仅调试时编译#endif}return0;}十二、头文件的包含头文件包含是预处理阶段的重要操作用于将其他文件的内容插入到当前文件中。12.1 包含方式及查找策略本地文件包含#include filename查找策略先在源文件所在目录查找若未找到再到标准库目录查找。库文件包含#include filename.h查找策略直接到标准库目录查找效率更高。注意库文件也可以用包含但会降低查找效率且不易区分是本地文件还是库文件。12.2 避免头文件重复包含头文件被多次包含会导致代码冗余、编译时间增加甚至出现重复定义错误。解决方法是使用条件编译方式一ifndef/define/endif// test.h#ifndef__TEST_H__// 如果未定义__TEST_H__#define__TEST_H__// 定义__TEST_H__// 头文件内容函数声明、结构体定义等voidtest();structStu{intid;charname[20];};#endif// __TEST_H__方式二#pragma once更简洁的方式直接在头文件开头添加#pragmaonce// 确保头文件只被包含一次// 头文件内容十三、其他预处理指令除了上述内容C语言还有一些其他预处理指令#error在编译时输出错误信息终止编译#pragma用于向编译器提供额外信息如#pragma pack()控制结构体对齐#line修改当前行号和文件名

相关新闻

C++实现毕业生实习就业管理系统:从需求分析到文件持久化实战

C++实现毕业生实习就业管理系统:从需求分析到文件持久化实战

1. 项目概述与核心价值最近在整理带学生做毕业设计的资料,发现一个挺有意思的现象:很多计算机相关专业的学生,在选题时总想搞点“高大上”的东西,比如AI、区块链、大数据平台,结果往往因为基础不牢、时间有限&#xff…

2026/10/21 10:21:22 阅读更多 →
Soofi S 30B-A3B:混合Mamba-Transformer架构在长文本处理中的工程实践

Soofi S 30B-A3B:混合Mamba-Transformer架构在长文本处理中的工程实践

上周,一个来自德国的开源项目在开发者社区里悄悄传开。不是因为铺天盖地的宣传,而是因为它的基准测试结果——在多个权威榜单上,这个名为Soofi S 30B-A3B的模型表现出了令人意外的竞争力。 更值得关注的是它的架构选择。在大家都在堆Transfo…

2026/10/21 13:23:48 阅读更多 →
深度解析Rhino.Inside.Revit:打破BIM与参数化设计壁垒的终极指南

深度解析Rhino.Inside.Revit:打破BIM与参数化设计壁垒的终极指南

深度解析Rhino.Inside.Revit:打破BIM与参数化设计壁垒的终极指南 【免费下载链接】rhino.inside-revit This is the open-source repository for Rhino.Inside.Revit 项目地址: https://gitcode.com/gh_mirrors/rh/rhino.inside-revit 你是否曾在Revit中为创…

2026/10/21 11:27:32 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/22 10:18:38 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/22 11:01:53 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/22 9:44:47 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/22 10:42:00 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/22 10:40:08 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/22 10:56:37 阅读更多 →

日新闻

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/22 1:47:45 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/22 21:33:42 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/22 5:12:39 阅读更多 →

月新闻

我发现了一个新思路:用 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/22 22:56:44 阅读更多 →
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/22 22:56:12 阅读更多 →
黑夜航拍船只数据集训练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/22 19:06:46 阅读更多 →