【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/7/19 10:22:24 阅读更多 →
Soofi S 30B-A3B:混合Mamba-Transformer架构在长文本处理中的工程实践

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

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

2026/7/19 10:22:24 阅读更多 →
深度解析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/7/19 10:22:24 阅读更多 →

最新新闻

2026吉他选购避坑指南!吃透3大核心,7款高性价比机型实测推荐

2026吉他选购避坑指南!吃透3大核心,7款高性价比机型实测推荐

很多零基础新手选购吉他时,都会陷入误区,其实新手选吉他的核心逻辑很简单:规避选购陷阱、贴合自身需求、追求极致性价比。本文结合多年乐器测评经验与海量用户反馈,拆解3大核心选购要点,揭秘市面常见商家套路&#xff…

2026/7/19 15:17:10 阅读更多 →
Continue + VS Code自动化编程方案框架

Continue + VS Code自动化编程方案框架

当前LLM的能力越来越强,结合Continue、VS等工具,以具备备独立编程和完成项目的能力。 比如将Continue与 VS Code 内置的测试探针(Test Adapter)、终端输出和 Lint 信息深度绑定。 这里尝试提供一套从手动闭环到全自动闭环的自动编程框架方案。 1 基础…

2026/7/19 15:17:10 阅读更多 →
一文讲透萨克斯的构造与发声秘密,附四款高口碑萨克斯推荐

一文讲透萨克斯的构造与发声秘密,附四款高口碑萨克斯推荐

一、萨克斯的三大核心部件一把完整的萨克斯主要由吹嘴(笛头)、脖管、管身三大部分构成。如果再细分,还可以拆出哨片、哨箍、盖帽、挂带等附件。但最核心的,永远是这三样。1. 吹嘴(笛头)吹嘴是萨克斯的起点&…

2026/7/19 15:17:10 阅读更多 →
思维模式切换技巧:提升Intern-S2-Preview-397B推理质量的简单方法

思维模式切换技巧:提升Intern-S2-Preview-397B推理质量的简单方法

思维模式切换技巧:提升Intern-S2-Preview-397B推理质量的简单方法 【免费下载链接】Intern-S2-Preview-397B 项目地址: https://ai.gitcode.com/InternLM/Intern-S2-Preview-397B Intern-S2-Preview-397B作为一款强大的AI模型,其推理质量很大程度…

2026/7/19 15:17:10 阅读更多 →
Linux PipeWire深度解析之pw_thread_loop_start调用流程与实战(十六)

Linux PipeWire深度解析之pw_thread_loop_start调用流程与实战(十六)

简介: CSDN博客专家、《Android系统多媒体进阶实战》作者 博主新书推荐:《Android系统多媒体进阶实战》🚀 Android Audio工程师专栏地址: Audio工程师进阶系列【原创干货持续更新中……】🚀 Android多媒体专栏地址&a…

2026/7/19 15:17:10 阅读更多 →
5分钟彻底优化Windows 11:告别臃肿系统,重获隐私与性能掌控权

5分钟彻底优化Windows 11:告别臃肿系统,重获隐私与性能掌控权

5分钟彻底优化Windows 11:告别臃肿系统,重获隐私与性能掌控权 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes t…

2026/7/19 15:16:09 阅读更多 →

日新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/19 0:00:40 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:00:40 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:00:40 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/19 0:00:40 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:00:40 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:00:40 阅读更多 →

月新闻