C++新经典02--编译预处理

news2024/11/24 5:31:09

在这里插入图片描述

编译预处理

编译阶段会做如下几件事:
(1)预处理。
(2)编译。包括词法分析、语法分析、目标代码生成、优化等。
(3)汇编。产生.o(.obj)目标文件。

C语言一般提供三种预处理功能:宏定义、 文件包含、条件编译。这三种预处理功能也是通过在源程序文件写入代码来实现的,只不过这些代码比较特殊,都是以“#”开头。

宏定义

不带参数的宏定义

有几点说明:
①宏名一般用大写字母表示,这是一种习惯,建议遵照这个习惯。
②宏定义其实并不是C语言语句(虽然有时候会称其为语句),不必在行末加分号,如果加分号则连分号一起被替换了。
③#define命令出现在程序中函数的外面,宏名的有效范围是#define之后到本源程序文件结束,不能跨文件使用,如果在另外一个源程序文件中使用,则需要在另外一个源程序文件中也做相同定义,或者把这些#define定义统一放到一个公共文件(如.h头文件)里,并用#include把这个公共文件包含到每个源程序文件中去。一般来说,#define命令都写在源程序文件开头部分,函数之前。
④可以用#undef命令终止宏定义的作用域,不过#undef命令用得比较少。
⑤用#define进行宏定义时,还可以引用已定义的宏,可以层层置换。
⑥字符串内的字符即便与宏名相同,也不进行替换。

带参数的宏定义

带参数的宏定义,就不仅是进行简单的内容替换,还要进行参数替换。其一般形式为:
在这里插入图片描述
作用:用右边的“被替换的内容”代替“宏名(参数表)”,在被替换的内容中,一般都会包含参数表中所指定的参数。看看如下范例:

#define S(a,b) a*b
.....
int Area = S(3,2);

在上面的范例中,用了宏S(3,2),系统是怎样替换的呢?把3、2分别代替宏定义中的形参a、b,最终用32替换了S(3,2)。所以程序代码“intArea=S(3,2);”就等价于“int Area=32;”。

带参数的宏定义展开置换的总结:对一般形式中提到的“被替换的内容”,要从左到右处理。如果“被替换的内容”中有“宏名”后列出的形参,如a、b,则将程序代码中相应的实参(可以是常量、变量或者表达式)代替形参,如果“被替换的内容”中的项并不是“宏名”后列出的形参,则保留,如上面ab中的“”就会被保留。看看如下范例:

#define PI 3.1415926
#define S(r) PI*r*r
int main()	//主函数
{
	float area;
	area S(3.6);
	......
}

有几点说明:(1)如果代码中出现“area=S(1+5);”,替换后会变成3.14159261+51+5,这肯定不对,程序代码的原意是替换后变成3.1415926*(1+5)*(1+5)。为了解决这个问题,要在形参外面加一个括号。如下所示:
在这里插入图片描述

总结一下:
(1)函数调用是先求出实参表达式的值,然后传递给形参,带参数的宏只进行简单的内容替换,宏展开时并不求值,如上面的S(1+5),宏展开时并不求1+5的值,只是原样用实参替换掉形参。
(2)函数调用是在程序运行阶段执行到该函数时才执行其中的代码,这涉及比如为所调用的函数分配临时内存等一系列工作。但宏展开是在编译阶段进行的,而且展开时也并不分配内存,当然也不存在“值传递”“返回值”等只有在函数调用中才存在的说法。
(3)宏的参数没有类型这个说法,只是一个符号,展开时用指定内容替换。例如#defineS(r)PI*(r)(r),其中的r是没有类型这种概念和说法的。
(4)宏展开每进行一次,源程序代码都会有所增多,如“area=S(1+5);”,在宏展开时会被替换成“area=3.1415926
(1+5)*(1+5);”,显然代码变多了,所以使用宏的次数如果增多,源程序代码就会增多,但函数调用不会使源程序代码增多。
(5)宏展开只占用编译时间,不占用运行时间,而函数调用占用运行时间(分配内存、传递参数、执行函数体、返回值等)。

文件包含

所谓“文件包含”,是指一个文件可以将另外一个文件的全部内容包含进来,也就是将另外的文件包含到本文件中。C语言中,通过#include命令来实现。其一般形式如下:
在这里插入图片描述

虽然可以用#include把任何一个其他文件的内容包含到当前文件中,但是在C语言中,最常见的做法还是一些源程序文件(扩展名为.c或者.cpp等)用#include把一些头文件(扩展名为.h或者.hpp等)包含进来,这种包含的感觉如图8.2所示。
在这里插入图片描述
在图8.2中,a.cpp源程序文件中原有的内容为A,最上面有一个#include “head.h”,这意味着head.h文件中的内容B也属于a.cpp,所以最终a.cpp的完整内容应该是内容B在上面(因为#include语句在上面),内容A在下面,要认识到,使用了#include之后,就等价于把其他文件的内容包含到当前文件中来,所以当前文件的程序代码长度增加了。

#include命令非常有用,可以节省大量的重复劳动,可以把一些公用的内容写成一个文件,如前面讲过的宏定义,就可以写成一个公用文件(一般是一个.h头文件),然后每个其他的源程序文件都通过#include命令将这个公用文件包含进来。

一般来说,#include都是#include一个.h文件,很少出现#include一个.cpp(源程序文件)的情形。

,.h文件一般称为头文件,因为h代表head(头)的意思。常把一些宏定义、函数说明,甚至一些公共的#include命令、外部变量说明(extern)等,都写在一个.h头文件里,然后在源程序文件中#include这个.h文件即可。

#include所包含的文件名可以用"",也可以用<>,它们有什么区别吗?回忆一下:
· <>是去系统目录(Visual Studio知道系统目录在哪儿)中找所包含的文件,所以诸如要包含标准的stdio.h头文件(系统提供的)就用<>,如#include<stdio.h>。
· "“的含义是首先在当前目录查找要包含的文件,如果找不到,再到系统目录中查找。所以”"常用于自己写的一些想被其他文件#include的文件,让系统优先到当前目录中寻找所要包含的文件(因为自己写的这些被包含文件往往会放到当前目录)。

条件编译

一般情况下,在生成可执行文件的过程中,源程序文件中的所有代码行都参加编译,但有时候希望对其中的一部分内容只在满足一定的条件下才进行编译,也就是对一部分内容指定编译的条件,也有的时候,希望当满足某条件时对一组语句进行编译,而当条件不满足时编译另外一组语句,这都叫条件编译。

条件编译用得也比较频繁,尤其是写一些跨操作系统平台的代码,例如这个代码既要求能在Windows下编译运行,也能在Linux下编译运行,但程序代码中有些特殊的系统调用函数只能在Windows下编译运行或者只能在Linux下编译运行,此时,就有必要使用条件编译。

条件编译用得也比较频繁,尤其是写一些跨操作系统平台的代码,例如这个代码既要求能在Windows下编译运行,也能在Linux下编译运行,但程序代码中有些特殊的系统调用函数只能在Windows下编译运行或者只能在Linux下编译运行,此时,就有必要使用条件编译:
在这里插入图片描述
作用:当标识符被定义过(#define来定义),则对程序段1进行编译,否则对程序段2进行编译。当然,“#else程序段2”这部分可以没有,此时的形式就变成:
在这里插入图片描述
在进行程序调试的时候,常常需要输出一些信息,调试完毕后,不再输出这些信息。看看如下范例:
在这里插入图片描述
然后在其他一些需要输出调试信息的地方(如main函数中),可以写类似如下代码:
在这里插入图片描述

(2)形式2。
在这里插入图片描述
作用:若标识符未被定义(未用#define来定义),则编译程序段1,否则编译程序段2。与形式1正好相反,看看如下范例:
在这里插入图片描述
然后在其他一些需要输出调试信息的地方(如main函数中),可以写类似如下代码:
在这里插入图片描述

(3)形式3。
在这里插入图片描述
作用:当指定的表达式值为真(非0)时就编译程序段1,否则编译程序段2,所以,事先给出一定的条件,就可以使程序代码在不同条件下进行不同程序段的编译。当然可以将上述形式扩展一下,引入#elif。如下:
在这里插入图片描述
作用:当表达式1值为真(非0)时就编译程序段1,否则当表达式2的值为真(非0)则编译程序段2,否则编译程序段3。
看看如下范例:
在这里插入图片描述
然后在其他一些需要输出调试信息的地方(如main函数中),可以写类似如下的代码:
在这里插入图片描述
从以上范例看起来,如果不用条件编译,似乎用if语句也可以做这些事情,那么用条件编译的好处是什么呢?
(1)最明显,条件编译可以减少目标程序长度。因为上面5行程序代码只相当于一行:
在这里插入图片描述
(2)项目开发也许会面临跨平台的问题,为了增加程序代码在各平台之间的可移植性,往往采用条件编译,如果不用条件编译,就很难解决同一套程序代码在Windows平台下和Linux平台下都能够在不修改源代码的情况下编译通过并生成可执行文件的问题。看看如下跨平台代码范例:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/893257.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5G智能网关助力打造数字乡村物联网

乡村振兴,网络先行,信息通信技术赋能农业农村现代化发展的作用日益彰显,也成为实施乡村振兴战略的重要力量。 截止今年三月,我国5G网络已经覆盖全国所有地级市、县城城区及数万行政村,农村光纤平均下载速率超过100Mb/s…

C#之枚举中的按位与()按位或(|)。

一些基础定义: 按位或运算符(|)是一种位运算符,用来对两个二进制数进行操作。对于每个位上的1,如果至少有一个二进制数中的对应位为1,则结果为1;否则,结果为0。按位与运算符&#x…

秒懂算法 | 汉诺塔问题与木棒三角形

在数学与计算机科学中,递归(recursion)是指一个过程或函数在其定义或说明中又直接或间接调用自身的一种方法。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算&#x…

JAVA权限管理 助力企业精细化运营

在企业的日常经营中,企业人数达到一定数量之后,就需要对企业的层级和部门进行细分,建立企业的树形组织架构。围绕着树形组织架构,企业能够将权限落实到个人,避免企业内部出现管理混乱等情况。权限管理是每个企业管理中…

【3Ds Max】弯曲命令的简单使用

简介 在3ds Max中,"弯曲"(Bend)是一种用于在平面或曲面上创建弯曲效果的建模命令。使用弯曲命令,您可以将对象沿特定轴向弯曲,从而创建出各种弯曲的几何形状。以下是使用3ds Max中的弯曲命令的基本步骤&…

多数据源项目serviceImpl中使用了@PostConstruct注解导致项目启动时报“表不存在”的解决办法

背景: 原多数据源项目一直运行好好的,这两天开发定时任务调度功能,开发完了调用定时任务一直提示找不到定时任务,调查半天发现没有提前把表中定义的定时任务加载到ScheduleJob,也就是需要执行以下操作: Pos…

获取屏幕共享视频流

文章目录 1.增加 html 的一行代码2. 在 static 目录下创建 js 文件夹&#xff0c;并在 js 文件夹下面创建 screen_share.js 文件3. 访问 htpps 的网址3.1 点击开始推流3.2 选择要分享的窗口3.3 停止推流 1.增加 html 的一行代码 <script src"/static/js/screen_share.…

不会吧,小红书竟然也偷用户作品训练自家AI模型?

图片来源&#xff1a;由无界AI生成 “画手在小红书发的图是可以随意被拿去炼的吗&#xff1f;” 最近一个月&#xff0c;有关“小红书偷用户作品炼自家AI模型”的话题不断发酵&#xff0c;用户在小红书以及其他社交平台发起抵制行动。一部分画师停止在小红书更新作品&#xff0…

安装nodejs

1.下载nodejs的压缩包 https://nodejs.org/download/release/latest-v16.x/ 这个是v16版本的&#xff0c;可以根据自己需求下载对应的版本&#xff1a; https://nodejs.org/download/release/ 2.解压&#xff0c;将nodejs放到固定的路径下 注意&#xff1a;不要包含中文路径 3.…

「隐语小课」拆分学习之“水平拆分学习”

一、引言 拆分学习是 2018 年由 MIT 最先提出的分布式算法。本文结合该领域的相关英文文献&#xff0c;介绍水平拆分学习的基本方法&#xff0c;同时还将对比拆分模型与中心化模型、联邦模型在不同条件下模型效率和准确性。拆分学习作为主流的隐私计算学习范式之一&#xff0c…

opencv 矩阵运算

1.矩阵乘&#xff08;*&#xff09; Mat mat1 Mat::ones(2,3,CV_32FC1);Mat mat2 Mat::ones(3,2,CV_32FC1);Mat mat3 mat1 * mat2; //矩阵乘 结果 2.元素乘法或者除法&#xff08;mul&#xff09; Mat m Mat::ones(2, 3, CV_32FC1);m.at<float>(0, 1) 3;m.at…

c语言——判断,判断是否是字母

//判断&#xff0c;判断是否是字母 #include<stdio.h> #include<stdlib.h> int main() {char c;printf("输入字符&#xff1a;");scanf("%c",&c);if((c>a&&c<z)||(c>A&&c<Z)) //a~z的ASCLL区间是97-122&…

【广州虚拟现实开发】VR智能中控系统进一步提高VR教学管理水平

随着科技的不断发展&#xff0c;虚拟现实(VR)技术已经逐渐走进了人们的生活。在教育领域&#xff0c;VR技术也得到了广泛的应用&#xff0c;尤其是在教学终端中控系统方面。那么&#xff0c;广州华锐互动开发的VR智能中控系统对学校有何益处呢&#xff1f; 首先&#xff0c;VR智…

【Nginx18】Nginx学习:WebDav文件存储与图片媒体处理模块

Nginx学习&#xff1a;WebDav文件存储与图片媒体处理模块 今天的内容怎么说呢&#xff1f;有两个感觉非常有意思&#xff0c;另外一些就差点意思。有意思的是&#xff0c;咱们可以直接用 Nginx 的 Webdav 功能搭建一个网盘&#xff0c;另外也可以实现动态的图片处理。这两个功能…

1AE4 的魔改混合放大电路

先上电路图&#xff1a; 最新的1AE4的电路&#xff0c;目标依旧是极致的音效。 因此&#xff0c;为了将1AE4的潜力榨干&#xff0c;采用了一些完全不同的思路&#xff1a; 1&#xff09;原有的屏极接地&#xff0c;因为是一个壳子&#xff0c;所以能起到很好的屏蔽作用&#…

图解算法--排序算法

目录 1.冒泡排序算法 2.选择排序算法 3.插入排序算法 4.希尔排序算法 5.归并排序算法 6.快速排序算法 1.冒泡排序算法 原理讲解&#xff1a; 从待排序的数组中的第一个元素开始&#xff0c;依次比较当前元素和它相邻的下一个元素的大小。如果当前元素大于相邻元素&#x…

7. 实现 API 自动生成

目录 1. pom.xml中引用依赖 2. 引入相关的依赖 3. 编写配置类 4. application.yml 中添加配置 5. API 常用注解 6. 访问 API 列表 7. API 导入 Postman 使用 Springfox Swagger生成 API&#xff0c;并导入 Postman&#xff0c;完成API单元测试。 Swagger 简介&#xff1a;Swag…

记录一个编译TubeTK时的报错:at_check问题

在使用如下命令安装TubeTK的cuda_nms时&#xff0c;报了一个错误&#xff0c;记录一下这个错误和解决办法 (base) redmeryredmery:~/Desktop/MOT/TubeTK/post_processing/nms$ python setup.py build_ext --inplace因为这个命令是在/home/redmery/Desktop/MOT/TubeTK/install/…

途乐证券-炒股开户流程是怎样的?

炒股是一种危险较大但收益也相对较高的出资方法&#xff0c;而开户则是出资炒股的前提。跟着科技的开展&#xff0c;炒股开户已经能够在线完结&#xff0c;但流程相对来说仍是比较繁琐的。那么&#xff0c;炒股开户流程是怎样的呢&#xff1f;下面从多个视点剖析。 一、炒股开户…

基于Servlet实现的管理系统(包含服务器源码+数据库)

资料下载链接 介绍 基于Servlet框架的管理系统 简洁版 &#xff1b; 实现 登录 、 注册 、 增 、 删 、 改 、 查 &#xff1b; 可继续完善增加前端、校验、其他功能等&#xff1b; 可作为 Servlet项目 开发练习基础模型&#xff1b; 课程设计 、 毕业设计 开发基础&…