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

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

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

2026/7/24 0:18:42 阅读更多 →
深度解析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/21 19:56:21 阅读更多 →

最新新闻

为什么93%的AI客服项目在第3个月失败?——资深架构师曝光未公开的流程断点诊断矩阵

为什么93%的AI客服项目在第3个月失败?——资深架构师曝光未公开的流程断点诊断矩阵

更多请点击: https://kaifayun.com 第一章:AI客服项目失败率的宏观归因与行业警示 近年来,全球企业AI客服项目平均失败率持续高于68%(据Gartner 2023年AI实施追踪报告),远超其他AI应用领域。这一高失败率…

2026/7/24 0:19:35 阅读更多 →
贝叶斯强化学习:原理、优势与工程实践

贝叶斯强化学习:原理、优势与工程实践

1. 贝叶斯强化学习的核心优势解析在传统强化学习中,智能体通过试错与环境交互来学习最优策略,这种方法虽然有效,但存在两个关键痛点:一是对不确定性的处理能力有限,二是样本效率低下。贝叶斯强化学习(Bayes…

2026/7/24 0:19:35 阅读更多 →
高光谱图像超分辨率重建技术解析与应用

高光谱图像超分辨率重建技术解析与应用

1. 高光谱图像超分辨率重建的现状与挑战高光谱图像超分辨率重建(Hyperspectral Image Super-Resolution, HSI-SR)是遥感图像处理领域的重要研究方向。与普通RGB图像不同,高光谱图像包含数百个连续的光谱波段,能够提供丰富的地物光…

2026/7/24 0:19:35 阅读更多 →
计算机毕业设计之基于.NET仓库管理系统的设计与实现

计算机毕业设计之基于.NET仓库管理系统的设计与实现

该系统充分利用MVC框架的简洁性、高效性和易用性,net语言,结合SQL Server数据库技术,构建了一个功能完善的仓库管理系统。系统涵盖了销售员、采购员、库管员、客户、供应商、货物库存、货物接收等多个核心模块,实现了货物库存的快…

2026/7/24 0:19:35 阅读更多 →
【Kimi论文辅助黄金公式】:1个提示词模板+2类学科适配策略+4步文献综述提速法

【Kimi论文辅助黄金公式】:1个提示词模板+2类学科适配策略+4步文献综述提速法

更多请点击: https://kaifayun.com 第一章:【Kimi论文辅助黄金公式】:1个提示词模板2类学科适配策略4步文献综述提速法 Kimi论文辅助黄金公式核心提示词模板 你是一位[学科领域]资深研究者,请基于以下要求协助我完成学术写作&a…

2026/7/24 0:18:35 阅读更多 →
本地大模型部署全攻略:从Ollama到企业级容器化

本地大模型部署全攻略:从Ollama到企业级容器化

1. 本地大模型部署方式全景解析在AI技术快速发展的当下,本地部署大模型已成为企业数字化转型和个人开发者探索AI能力的重要选择。与云端API调用相比,本地部署能够提供更高的数据隐私性、更低的长期使用成本以及更灵活的定制空间。目前主流的本地部署方式…

2026/7/24 0:18:35 阅读更多 →

日新闻

用Highcharts 创建可拖拽三维散点立方体3D图表

用Highcharts 创建可拖拽三维散点立方体3D图表

该案例基于Highcharts scatter3d 三维散点图实现空间立方体散点可视化,核心特色:三维 X/Y/Z 三轴空间,所有散点分布在 0~10 立方体空间内;散点使用径向渐变实现立体 3D 圆球质感;支持鼠标 / 触屏拖拽画布,…

2026/7/24 0:00:29 阅读更多 →
AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口 AppCertDlls 位于 HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls。本文的程序功能是只读列出这个键在 64 位和 32 位注册表视图中的全部值,并显示每条值的来源、名称、类型和可安全显示的数…

2026/7/24 0:00:29 阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:29 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/7/23 17:49:47 阅读更多 →

月新闻