编译预处理

2023-11-02

声明:经过长时间的学习,对宏定义尤其是条件编译这块存在盲区,特此整理笔记:此次内容参考《c++ 程序设计教程(第三版)》:清华大学出版社

一、编译预处理

编译预处理是在编译源程序之前,由预处理器对源程序进行加工处理工作,所谓预处理器,是包含在编译器中的预处理程序。如图所示:
在这里插入图片描述

源程序的中的编译预处理命令一律以#开头回车键结束,每条命令占一行,并且通常放在源程序的开始部分。

编译预处理的作用是将源程序文件中的预处理命令进行处理,生成一个中间文件,编译系统再对此中间文件进行编译并生成目标代码,最后生成的目标代码中不含有预处理命令;

c++提供的预处理功能主要有宏定义、文件包含、条件编译,前两种比较常见,主要介绍最后一种

二、宏定义

2.1不带参数的宏定义

用一个指定的标识符来代表一个字符串,这个指定的标识符称为宏名,格式:

#define <宏名>  <字符串>

#define PI 3.1415926  
/*
*1.在进行预编译后,将程序中的所有宏名PI替换成3.1415926
*2.#define 定义的宏有效范围为定义处至本源文件结束,可以使用undef 命令终止宏定义的作用域
*3.宏定义语句的行末一般不加分号,因为他仅具有替换功能,并不是代码语句
*/

#define PI 3.1415926
/*
	//do something;
*/
#undef PI	//终止宏定义的作用域

2.2带参数的宏定义

宏名也可以带参数定义,支持参数替换,其格式为:

#define <宏名(参数列表)> <字符串>

#define  S(a,b) a*b
/*
*参数表中的参数a,b称为宏名的形式参数,源程序语句中的宏名仍使用右端字符串替换,只不过替换时将字符串中的
*形式参数用语句中宏名所带的实际参数取代而已;
*/
double area;
area=S(3,2);
/*此时,3为a的实际参数,2为b的实际参数,则宏名S(3,2)经处理后,用字符串3*2替换再进行编译。即S(a,b)等同于a*b*/

/*带参数的宏替换过程可描述过程如下:*/

按#define命令行中指定的字符串从左至右置换宏名,字符串中的形参以相应的实参代替,字符串中非形参字符保持不变。

2.3带参数的宏定义与函数调用的区别

2.3.1.宏定义与函数调用计算机处理的时间不同

宏定义是编译预处理,计算机还未正式编译程序,就先处理预处理命令,因此,宏定义的实参对形参的代入是“机械”替换,同时宏定义的形参也不需要标注数据类型,因为系统这时候还根本不“认识”这些参数的类型,不进行语法检查,跟谈不上为其分配内存单元。

函数调用是在程序已经编译连接成为二进制码可执行程序后,在执行这个函数时发生的,此时需要分配形参的存储单元,完成实参到形参的具体值的传递,所以函数的形参要定义数据类型,且有时函数需要返回一个具体的值,函数也需要有明确的类型说明。

2.3.2.宏定义与函数调用的侧重点不同

宏定义在程序运行期间时已经不存在了,所以其目的仅仅是为了书写时使程序清晰、简洁。宏展开只占用编译时间,不占用运行时间,宏展开后使源程序增长,占用程序的“空间”
被调函数无论被调用了多少次,其生成的机器代码长度是不变的。但当程序运行时,每次调用该函数时,系统都要完成调用前的准备和收尾工作,如分配形参单元,保存调用时刻现场地址、参数、值传递、函数值返回、恢复现场,这些工作都要占用一定的运行时间,因此函数调用占用程序的“时间”

三、文件包含

文件包含用#include命令,预处理后将指令中指明的源程序文件嵌入到当前源文件的指令位置处,格式为:

#include <文件名>
//or
#include "文件名"

在这里插入图片描述

含有文件包含命令的源程序,在编译预处理时将所包含的文件全部内容复制到该命令行处,生成一个中间文件,然后编译连接这个文件,得到实际上包含两个(或多个)源程序的可执行程序
注意:
1.一个#include命令只能包含一个文件。
2.文件包含命令所包含的文件必须是文本文件(ASCII)文件,一般是c++源文件或系统库文件,不可以是可执行程序(.exe)或者是目标程序(.obj)

3.1 include包含格式;

文件包含有两种格式,用#include<文件名>的格式时,预处理编译器就直接到存放c++系统的文件目录中查找要包含的文件,若找不到,编译系统给出出错信息。一般来说系统的标准头文件和库文件都是用这种方式包含的,如#include。
用#include"文件名"的格式时,预编译处理器首先在当前编译文件所在目录进行搜索,如果找不到,再去c++系统目录中进行搜索;也可以在包含命令中指定首先搜索的目录,如#include"/home/.../file2.cpp",可指定在"/home/.../"目录下搜索file2.cpp,若找不到,再去c++系统目录下搜索;如果还找不到,就给出出错信息。<一般这种方式用来包含用户自己建立的文件>

1.用#include"文件名"的格式时,预编译处理器首先在当前编译文件所在目录进行,或给定目录进行搜索,如果找不到再去c++系统目录**中进行搜索,如果还找不到,就给出出错信息。
2.用#include<文件名>的格式时,预处理编译器就直接到存放c++系统的文件目录中查找要包含的文件,若找不到,编译系统给出出错信息`

四、条件编译

在通常情况下,源程序中所有的语句都要被编译,生成机器码可执行程序。但有时希望源程序的某些语句在满足一定条件下才被编译,或者说对源程序的部分语句有选择进行编译。

条件编译指令有两类:
一是根据宏名是否定义来确定是否编译某些程序段;另一类是根据表达式的值来确定被编译的程序段;

4.1宏名作为编译的条件

#ifdef	宏名
	程序段1
<#else
	程序段2>
#endif

/*#ifdef 与ifndef 作用一样,只是选择的条件相反*/

其中<>中的内容可以省略。
如果源程序前面的定义了宏名,则编译程序段1,否则编译程序段2;
例如,在调试程序时,常常需要输出一些变量在程序运行过程中的值,以此来检查程序运行中的错误,而调试完成后不需要输出这些信息,就可以将输出这些值的语句段作为条件语句;

#include<iostream>
using namespace std;
int main()
#define DEBUG	//定义了宏名DEBUG
{
	int n = 5, s = 1;
	for (int i = 0; i < n; i++)
	{
		s = s * i;
#ifdef DEBUG	//如果有定义则编译
		cout << "i=" << i << '\t' << "s=" << s << endl;		//条件编译语句
#endif			//条件编译语句结束

	}
	cout << "n=" << s << endl;
	return 0;
}

输出:
i=0 s=0
i=1 s=0
i=2 s=0
i=3 s=0
i=4 s=0
n=0

#include<iostream>
using namespace std;
int main()
//#define DEBUG	//注释该行,即该行不成立
{
	int n = 5, s = 1;
	for (int i = 0; i < n; i++)
	{
		s = s * i;
#ifdef DEBUG	//条件编译的条件不成立
		cout << "i=" << i << '\t' << "s=" << s << endl;		//该行不被编译,不生成机器码
#endif			

	}
	cout << "n=" << s << endl;
	return 0;
}

输出:
n=0

2.表达式的值作为编译条件

#ifdef	表达式
	程序段1
<#else
	程序段2>
#endif

其中<>中的内容可以省略。
如果表达式的值非0(表达式应该是一些常量的计算),则编译程序段1,否则编译程序段2;

#include<iostream>
using namespace std;
int main()
{
	int n = 5, s = 1;
	for (int i = 0; i < n; i++)
	{
		s = s * i;
#if 1 //为真,执行
		cout << "i=" << i << '\t' << "s=" << s << endl;		
#endif			

	}
	cout << "n=" << s << endl;
	return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编译预处理 的相关文章

  • 在C语言中使用“void”

    我很困惑为什么我们需要通过void转换为 C 函数 int f void return 0 versus int f return 0 什么是正确的做法以及为什么 In C int f 是一种老式的声明 它说f需要固定但未指定数量和类型的参
  • 在搜索 List 时,为什么 Enumerable.Any(Func predicate) 比带有 if 语句的 foreach 慢

    最近有件事引起了我的好奇心 Why is the Enumerable Any Func
  • 我的线程图像生成应用程序如何将其数据传输到 GUI?

    Mandelbrot 生成器的缓慢多精度实现 线程化 使用 POSIX 线程 Gtk 图形用户界面 我有点失落了 这是我第一次尝试编写线程程序 我实际上并没有尝试转换它的单线程版本 只是尝试实现基本框架 到目前为止它是如何工作的简要描述 M
  • 为什么我不能用 `= delete;` 声明纯虚函数?

    Intro 纯虚函数使用通用语法声明 virtual f 0 然而 自 c 11 以来 有一种方法可以显式地传达non existence 特殊 成员函数的 Mystruct delete eg default constructor Q
  • C# 中的接口继承

    我试图解决我在编写应用程序时遇到的相当大的 对我来说 问题 请看这个 为了简单起见 我将尝试缩短代码 我有一个名为的根接口IRepository
  • 构造函数中显式关键字的使用

    我试图了解 C 中显式关键字的用法 并查看了这个问题C 中的explicit关键字是什么意思 https stackoverflow com questions 121162 但是 那里列出的示例 实际上是前两个答案 对于用法并不是很清楚
  • C++ 异步线程同时运行

    我是 C 11 中线程的新手 我有两个线程 我想让它们同时启动 我可以想到两种方法 如下 然而 似乎它们都没有按照我的预期工作 他们在启动另一个线程之前启动一个线程 任何提示将不胜感激 另一个问题是我正在研究线程队列 所以我会有两个消费者和
  • 从多个类访问串行端口

    我正在尝试使用串行端口在 arduino 和 C 程序之间进行通信 我对 C 编程有点陌生 该程序有多种用户控制形式 每一个都需要访问串口来发送数据 我需要做的就是从每个类的主窗体中写入串行端口 我了解如何设置和写入串行端口 这是我的 Fo
  • 访问者和模板化虚拟方法

    在一个典型的实现中Visitor模式 该类必须考虑基类的所有变体 后代 在许多情况下 访问者中的相同方法内容应用于不同的方法 在这种情况下 模板化的虚拟方法是理想的选择 但目前这是不允许的 那么 模板化方法可以用来解析父类的虚方法吗 鉴于
  • 如何从 C# 控制器重定向到外部 url

    我使用 C 控制器作为网络服务 在其中我想将用户重定向到外部网址 我该怎么做 Tried System Web HttpContext Current Response Redirect 但没有成功 使用控制器的重定向 http msdn
  • 在 2D 中将一个点旋转另一个点

    我想知道当一个点相对于另一个点旋转一定角度时如何计算出新的坐标 我有一个块箭头 想要将其相对于箭头底部中间的点旋转角度 theta 这是允许我在两个屏幕控件之间绘制多边形所必需的 我无法使用和旋转图像 从我到目前为止所考虑的情况来看 使问题
  • 基于xsd模式生成xml(使用.NET)

    我想根据我的 xsd 架构 cap xsd 生成 xml 文件 我找到了这篇文章并按照说明进行操作 使用 XSD 文件生成 XML 文件 https stackoverflow com questions 6530424 generatin
  • 当模板类不包含可用的成员函数时,如何在编译时验证模板参数?

    我有以下模板struct template
  • 获取 2 个数据集 c# 中的差异

    我正在编写一个简短的算法 它必须比较两个数据集 以便可以进一步处理两者之间的差异 我尝试通过合并这两个数据集并将结果更改放入新的数据集来实现此目标 我的方法如下所示 private DataSet ComputateDiff DataSet
  • 在类的所有方法之前运行一个方法

    在 C 3 或 4 中可以做到这一点吗 也许有一些反思 class Magic RunBeforeAll public void BaseMethod runs BaseMethod before being executed public
  • 为什么拆箱枚举会产生奇怪的结果?

    考虑以下 Object box 5 int int int box int 5 int nullableInt box as int nullableInt 5 StringComparison enum StringComparison
  • 运算符“==”不能应用于“int”和“string”类型的操作数

    我正在编写一个程序 我想到了一个数字 然后计算机猜测了它 我一边尝试一边测试它 但我不断收到不应该出现的错误 错误是主题标题 我使用 Int Parse 来转换我的字符串 但我不知道为什么会收到错误 我知道它说 不能与整数一起使用 但我在网
  • 我在在线程序挑战编译器中遇到演示错误

    include
  • 用于 C# XNA 的 Javascript(或类似)游戏脚本

    最近我准备用 XNA C 开发另一个游戏 上次我在 XNA C 中开发游戏时 遇到了必须向游戏中添加地图和可自定义数据的问题 每次我想添加新内容或更改游戏角色的某些值或其他内容时 我都必须重建整个游戏或其他内容 这可能需要相当长的时间 有没
  • 匿名结构体作为返回类型

    下面的代码编译得很好VC 19 00 23506 http rextester com GMUP11493 标志 Wall WX Za 与VC 19 10 25109 0 标志 Wall WX Za permissive 这可以在以下位置检

随机推荐

  • /bin/sh: 1: nvcc: not found Makefile:89: recipe for target 'obj/convolutional_kernels.o' failed

    在测试YOLO时出现 nvcc gencode arch compute 30 code sm 30 gencode arch compute 35 code sm 35 gencode arch compute 50 code sm 50
  • 高性能内存分配器 jemalloc 基本原理

    Netty 内存管理的实现并不是一蹴而就的 它也是参考了 jemalloc 内存分配器 今天我们就先介绍 jemalloc 内存分配器的基本原理 为我们后面的课程打好基础 背景知识 jemalloc 是由 Jason Evans 在 Fre
  • [Warning] ‘typedef‘ was ignored in this declaration解决

    首先先展示一下问题是怎么样的 出现了 typedef was ignored in this declaration的问题 下面提供解决方案及原因 原因 在于使用结构体的时候没有使用别名 解决方案 1 去掉typedef 2 在结构体后面加
  • elementui tree怎样设置默认勾选

    在树形菜单数据里面设置一个唯一的字段 这里我的就是id字段 然后设置node key为那个唯一字段 然后用default checked keys绑定一个数组 这个数组里面就是需要勾选的菜单所对应的的id 比如我这里的是9 这样运行之后菜单
  • echarts使用结合时间轴timeline动态刷新案例

    1 echarts简介 ECharts 一个使用 JavaScript 实现的开源可视化库 可以流畅的运行在 PC 和移动设备上 兼容当前绝大部分浏览器 IE8 9 10 11 Chrome Firefox Safari等 底层依赖轻量级的
  • unity的UGUI的mask(遮罩)的使用

    之前我写过一篇博客关于UGUI的优化 其中提到了Mask的使用会增加性能的消耗 但是在一些情况下 使用这个会有奇效 比如小地图 Minimap 的开发 这篇博客介绍一下UGUI中的Mask的使用方法 很简单的 首先创建一个 Image 给他
  • 【PHP发送邮件】PHP实现发送邮件

    PHP发送邮件 Thinkphp直接使用 其他框架修改使用 1 安装 composer require phpmailer phpmailer 2 填写配置表 配置文件mail php
  • MQTT协议介绍

    1 MQTT协议简介 MQTT Message Queuing Telemetry Transport 消息队列遥测传输 是一个轻量的发布 订阅模式消息传输协议 是专门针对低带宽和不稳定网络环境的物联网应用设计的 特点 1 开放消息协议 易
  • Spring Data HelloWorld(三)

    在 Spring Data 环境搭建 二 的基础之上 我们改动 定义个一个接口 继承Repository类 咱们先实现一个根据名字查询 package org springdata repository import org springd
  • Python开发篇——添加mysqlclient

    最近使用mysql8 0 于是我就尝试用Django的框架 但是执行poetry add mysqlclient却出现了错误 python3 7 dison dison X450LD workstation project script s
  • Kuberneters企业级容器云平台落地实践之二

    九 日志中心 1 filebeat安装 Filebeat 是一个用于转发和集中日志数据的轻量级传送器 作为代理安装在您的服务器上 Filebeat 监控您指定的日志文件或位置 收集日志事件 并将它们转发到Elasticsearch或 Log
  • Linux下装载Qt

    Linux下装载Qt 官网文件下载Qt 本官网地址 http download qt io archive qt 5 9 5 9 6 https www qt io offline installers 将文件放置Linux目录下 将随意一
  • 6.SpringBoot Web开发-webjars&静态资源映射规则(欢迎页和角标favicon.ico替换)

    文章总结 作为一个后端开发 在Springboot中怎样引入需要的js依赖以及常用的静态资源映射呢 SpringBoot已经给做好了自动化配置 使用时只需要按照默认的配置去放相应的文件 就可以快速上手 1 创建SpringBoot web项
  • ArrayList,List 的区别;

    List 的区别 6 数组 数组 内存中是连续存储的 索引速度非常快 赋值与修改元素也很简单 但不 利于动态扩展以及移动 ArrayList 因为数组的缺点 就产生了 ArrayList ArrayList 使用该类时必须进行引用 同时继承
  • 给语音信号加混响的常用方法(方法一)

    使用python包 pyroomacoustic 给干净语音加混响 Pyroomacoustics是一款旨在快速开发和测试音频阵列处理算法的软件包 包的内容可分为三个主要组成部分 1 直观的Python面向对象接口 可快速构建2D和3D房间
  • 2020年10月蓝桥杯原题寻找2020

    这题比较简单 主要是以行 列 斜线的方式寻找2020的个数 注意的一点就是控制下标越界的情况 答案是16520 public class test4 public static String readTxt File file String
  • 计算机英文专业文献翻译,计算机专业英文文献翻译.doc

    文档介绍 计算机英文文献翻译 计算机英文文献翻译 INDUSTTRY PERSPECTIVE USING A DSS TO KEEP THE COST OF GAS DOWN Think you spend a lot on gas for
  • JDBC连接postgresql例子

    package tool import java sql Connection import java sql DriverManager import java sql PreparedStatement import java sql
  • 93 Three.js 使用设置normalMap创建更加细致的凹凸和褶皱

    案例查看地址 http www wjceo com blog threejs 2018 05 03 156 html 左边为设置normalMap后的效果 右边为正常效果 我们会发现设置了normalMap后的立体感非常的强烈 简介 法线贴
  • 编译预处理

    声明 经过长时间的学习 对宏定义尤其是条件编译这块存在盲区 特此整理笔记 此次内容参考 c 程序设计教程 第三版 清华大学出版社 一 编译预处理 编译预处理是在编译源程序之前 由预处理器对源程序进行加工处理工作 所谓预处理器 是包含在编译器