【C语言进阶】程序编译中的预处理操作

📚作者简介:爱编程的小马,正在学习C/C++,Linux及MySQL..

📚以后会将数据结构收录为一个系列,敬请期待

● 本期内容讲解C语言中程序预处理要做的事情

目录

1.1 预处理符号

1.2 #define

1.2.1  #define定义标识符

1.2.2 #define定义宏

1.2.3 #define替换规则

1.3 #和## 

1.4 带副作用的宏参数 

1.5 宏与函数的区别与联系


1.预处理详解

1.1 预处理符号

__FILE__     //进行编译的源文件

__LINE__    //文件当前的行号

__DATE__  //文件被编译的日期

__TIME__   //文件被编译的时间

__STDC__  //如果编译器遵循ANSIC,其值为一,否则未定义

打印出来看一看,很明显,vs2019不支持ANSIC 

1.2 #define

1.2.1  #define定义标识符

 语法:#define name stuff 

#define MAX 100     //定义MAX的值为100
#define reg register //为register这个关键字创建简短的名字
#define ro for(;;)   //打印for这个死循环
#define CASE break;case  //在case语句后换成break;case
#define debug_print printf("file:%s\tline:%d\t\
							 date:%s\ttime:%s\t",__FILE__,__LINE__,__DATE__,__TIME__);   
#include<stdio.h>
int main()
{
	printf("%d\n", MAX);
	debug_print;
	//ro
	//{
	//	//printf("hehe\n");
	//}
	//int a = 0;
	//switch (a)
	//{
	//case1:  //不推荐这种写法,可读性太差
	//CASE2:
	//CASE3:
	//}
	return 0;
}

提问:

 在定义#define的时候,是否要加上;号

比如:

#define MAX1 100
#define MAX2 100;
#include<stdio.h>
int main()
{
	printf("%d\n", MAX1);
	printf("%d\n", MAX2);
	return 0;
}

究竟是MAX1可以还是MAX2可以呢?看看运行结果

解析:实际上在预处理的时候,会将#define定义的标识符进行替换,直接替换成表达式。比方说,MAX1处会被直接替换为100,MAX2处会被替换为100;在这个地方语句就出问题了,怎么两个分号呢?所以MAX2是不能这么写的。

1.2.2 #define定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种通常被称为宏或者定义宏

宏的定义:

#define name(parament-list) stuff 

如果两者之间有任何空白存在,那么参数列表就会解释为stuff的一部分

举个例子:

#define SQUARE(x) x*x
#include<stdio.h>
int main()
{
	int a = 5;
	int c = SQUARE(a);
	printf("%d\n", c);
	return 0;
}

上述代码输出一定是25,因为在预处理过程中会直接替换,int c = a*a ,那么我再问问大家,如果我传的是a+5呢? 

#define SQUARE(x) x*x
#include<stdio.h>
int main()
{
	int a = 5;
	int c = SQUARE(a+5);
	printf("%d\n", c);
	return 0;
}

 

解析:为什么我传递的是a+5是10,应该是输出100的,为什么最后打印了一个35?原因是这样的,还是替换 int c = a+5*a+5 ,输出就是35,怎么解决呢,要加括号。 

加上括号之后,就对了。

但是呢,我又突发奇想 ,又问大家一个问题,看下面这个代码,会输出什么?

#define DOUBLE(x) (x)+(x)
#include<stdio.h>
int main()
{
	int a = 5;
	int c = 10*DOUBLE(a + 5);
	printf("%d\n", c);
	return 0;
}

按理来说,调用DOUBLE这个宏应该是2x也就是20,再乘10就是200。那我们来看看是否是输出200呢?

 解析:可以看到输出的是110,并不是200,为什么呢?其实上就是替换 int c = 10*(a+5)+(a+5),实际上是输出110而不是我们想要的200,怎么办呢?其实还是加括号,再宏定义处再加括号即可

所以呢在创建宏的时候,一定要不要吝啬括号,一定要多加括号。

1.2.3 #define替换规则

在程序过程中扩展#define定义符号和宏时,需要涉及几个步骤:

1、在调用宏时,首先对参数进行检查,看看是否任何由#define定义的符号。如果是,它们首先被替换。

2、替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换

3、最后,再次对结果文件进行扫描,看看他是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

注意:

1、宏函数和#define定义中可以可以出现其他#define定义的符号。但是对于宏,不可以递归

2、当预处理器搜索宏的时候,字符串常量不被搜索。

可以看个例子:

#define DOUBLE(x) ((x)+(x))
#include<stdio.h>
int main()
{
	printf("DOUBLE(5)");
}

 

1.3 #和## 

(1)首先我们一起来看这样一段代码

#include<stdio.h>
int main()
{
	char* p = "hello bit\n";
	printf("%s", p);
	printf("hello ""bit");
	return 0;
}

 

可以看到,字符串是有自动连接的特点的。那么我有个设想,是说我们可以根据数据类型来打印对应的数据,比方说我给一个宏传 3 整形,他就可以给我打印出3;比方说我给一个宏传3.5这个浮点型,就可以给我打印出3.500000,看我如何实现

#define PRINT(FORMAT,VALUE) printf("the value of "#VALUE" is "FORMAT"\n",VALUE);
#include<stdio.h>
int main()
{
	int a = 10;
	//printf("the value of a is %d\n", a);
	PRINT("%d",a);
	return 0;
}

这个代码是可以这么理解,就是宏参数前面加上#号,可以把一个宏参数变成对应的字符串。

这个代码的输出结果就是,PRINT("%d",a)会被替换为printf("the value of "a" is "%d"\n",10)

 (2)##

##的作用是:

1、##可以把位于它两边的符号合成一个符号

2、它允许宏定义从分离的片段创建标识符

 举个栗子:

#include<stdio.h>

#define ping(num1,num2) num1##num2 
int main()
{
	int chinano1 = 2024;
	int r = ping(china,no1);
	printf("%d", r);
	return 0;
}

 

解析:宏会直接替换,int r = chinano1,所以最后会打印出2024。

1.4 带副作用的宏参数 

当宏参数在宏定义出现超过一次的时候,如果这个参数带有副作用,那么你使用宏参数的时候就会非常的危险,导致不可预测的后果。副作用就是表达式求值后出现永久性的效果。

副作用是什么呢?例如:

int x = 5;
//我想让y变成6,有几种方法
//1、
int y = x+1 //无副作用
//2、
int y = ++x; //有副作用的

举个栗子来证明下如果是带副作用的宏参数,会有什么问题:

#include<stdio.h>
#define MAX(a,b) ((a)>(b)?(a):(b))
int main()
{
	//传递有副作用的宏参数
	int a = 5;
	int b = 6;
	int c = MAX(a++,b++);
	printf("a = %d\n", a);
	printf("b = %d\n", b);
	printf("c = %d\n", c);
	return 0;
}

输出结果:

 

为什么是这个结果,不是6和7比较返回7吗?其实不是的,因为你调用了宏就是替换,

int c = (a++)>(b++)?(a++):(b++),可以看到,宏参数每往后走一步就加加,如果一直反复调用实际上是很危险的。

1.5 宏与函数的区别与联系

属性#define定义的宏函数
代码长度每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,其他的代码量会大幅增长函数代码只出现在一个地方,每次调用,都去那一个地方找
执行速度更快存在函数的调用与返回,要慢一些
操作符优先级宏的求值是要在上下文联系中的,除非加上括号,否则会产生不可改变的后果函数参数只在函数调用的时候求值一次,函数表达式结果更好预测
带有副作用的参数参数可能被替换到任意位置,所以带有副作用的参数会带来不可预料的结果

函数参数只在传参的时候求值一次,因此表达式结果更好预测

参数类型宏的参数与类型无关,只要表达式合法,都可以用函数参数对类型要求十分严格,如果函数类型不同,就需要不同的函数参数,即使他们执行的类型是相同的。
调试宏是不方便调试函数是可以逐语句调试的
递归宏是不能递归的函数是可以递归的

 


  总结

上文就是C语言编程预处理阶段的相关知识了,C语言进阶系列正式完结!!!

如果这份博客对大家有帮助,希望各位给小马一个大大的点赞鼓励一下,如果喜欢,请收藏一下,谢谢大家!!!
制作不易,如果大家有什么疑问或给小马的意见,欢迎评论区留言。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构(01)——链表OJ

目录 移除链表元素 思路1 不创建虚拟头节点 思路2 创建虚拟头节点 反转链表 寻找链表中间节点 判断链表是否相交 回文链表 环形链表 环形链表|| 移除链表元素 . - 力扣&#xff08;LeetCode&#xff09; 要想移除链表的元素&#xff0c;那么只需要将目标节点的前一…

07_for循环返回值while循环

文章目录 1.循环返回值2.yield接收for返回值3.scala调用yield方法创建线程对象4.scala中的while循环5.scala中的流程控制 1.循环返回值 for循环返回值是Unit 原因是防止产生歧义&#xff1b; 2.yield接收for返回值 // 2.yield关键字打破循环&#xff0c;可以使for循环输出…

智慧农业设备——虫情监测系统

随着科技的不断进步和农业生产的日益现代化&#xff0c;智慧农业成为了新时代农业发展的重要方向。其中&#xff0c;虫情监测系统作为智慧农业的重要组成部分&#xff0c;正逐渐受到广大农户和农业专家的关注。 虫情监测系统是一种基于现代传感技术、图像识别技术和大数据分析技…

面试笔记——线程池

线程池的核心参数&#xff08;原理&#xff09; public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)corePoolSize …

25计算机考研院校数据分析 | 四川大学

四川大学(Sichuan University)简称“川大”&#xff0c;由中华人民共和国教育部直属&#xff0c;中央直管副部级建制&#xff0c;是世界一流大学建设高校、985工程”、"211工程"重点建设的高水平综合性全国重点大学&#xff0c;入选”2011计划"、"珠峰计划…

PostgreSQL的学习心得和知识总结(一百四十)|深入理解PostgreSQL数据库 psql工具 \set 变量内部及HOOK机制

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

【能力展现】魔改ZXING源码实现商业级DM码检测能力

学习《OpenCV应用开发&#xff1a;入门、进阶与工程化实践》一书 做真正的OpenCV开发者&#xff0c;从入门到入职&#xff0c;一步到位&#xff01; 什么是DM码 dataMatrix是一种二维码&#xff0c;原名datacode&#xff0c;由美国国际资料公司于1989年发明。dataMatrix二维码…

GuildFi升级为Zentry的背后 链游公会的探索与转型

​链游即区块链游戏&#xff0c;指依托区块链技术构建的游戏产品。其与传统游戏的最大区别在于区块链的去中心化特性对玩家的资产有着天然的确权行为&#xff0c;因此玩家在链游中的资产是作为玩家的个人资产存在。较于 GameFi 来说&#xff0c;链游的包含范围更大&#xff0c;…

吴恩达机器学习笔记:第 8 周-14降维(Dimensionality Reduction) 14.3-14.5

目录 第 8 周 14、 降维(Dimensionality Reduction)14.3 主成分分析问题14.4 主成分分析算法14.5 选择主成分的数量 第 8 周 14、 降维(Dimensionality Reduction) 14.3 主成分分析问题 主成分分析(PCA)是最常见的降维算法。 在 PCA 中&#xff0c;我们要做的是找到一个方向…

【高校科研前沿】华东师大白开旭教授博士研究生李珂为一作在RSE发表团队最新成果:基于波谱特征优化的全球大气甲烷智能反演技术

文章简介 论文名称&#xff1a;Developing unbiased estimation of atmospheric methane via machine learning and multiobjective programming based on TROPOMI and GOSAT data&#xff08;基于TROPOMI和GOSAT数据&#xff0c;通过机器学习和多目标规划实现大气甲烷的无偏估…

OS复习笔记ch5-1

引言 讲解完进程和线程之后&#xff0c;我们就要来到进程的并发控制这里&#xff0c;这一章和下一章是考试喜欢考察的点&#xff0c;有可能会出大题&#xff0c;面试也有可能会被频繁问到&#xff0c;所以章节内容较多。请小伙伴们慢慢食用&#xff0c;看完之后多思考加强消化…

【JPE】顶刊测算-工业智能化数据(附stata代码)

数据来源&#xff1a;国家TJ局、CEC2008、IFR数据 时间跨度&#xff1a;2006-2019年 数据范围&#xff1a;各省、地级市 数据指标&#xff1a; 本数据集展示了2006-2019年各省、各地级市的共工业智能化水平的数据。本数据集包含三种构建工业机器人密度来反映工业智能化水平的方…

基于Springboot的数字化农家乐管理平台(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的数字化农家乐管理平台&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

Apache Seata基于改良版雪花算法的分布式UUID生成器分析2

title: 关于新版雪花算法的答疑 author: selfishlover keywords: [Seata, snowflake, UUID, page split] date: 2021/06/21 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 关于新版雪花算法的答疑 在上一篇关于新版雪花算法的解析中…

web前端学习笔记4

4. 盒子模型 4.0 代码地址 https://gitee.com/qiangge95243611/java118/tree/master/web/day044.1 什么是盒子模型(Box Model) 所有HTML元素可以看作盒子,在CSS中,"box model"这一术语是用来设计和布局时使用。 CSS盒模型本质上是一个盒子,封装周围的HTML元素,…

在Docker中部署Java应用:Java版本隔离的实践案例

在Docker中部署Java应用&#xff1a;Java版本隔离的实践案例 人生就是一场又一场的相遇&#xff0c;一个明媚&#xff0c;一个忧伤&#xff0c;一个华丽&#xff0c;一个冒险&#xff0c;一个倔强&#xff0c;一个柔软&#xff0c;最后那个正在成长。 背景需求 在软件开发和部…

Debian 12 -bash: netstat: command not found 解决办法

问题表现&#xff1a; debian 12系统中&#xff0c;不能使用 netstat命令 处理办法&#xff1a; netstat 命令就的net-tools中&#xff0c;把net-tools工具安装上就好了。 apt-get install netstat 安装之后就可以使用netstat 命令了&#xff0c;如查询端口情况&#xff1a; …

基于SpringBoot+Vue高校宣讲会管理系统设计与实现

项目介绍&#xff1a; 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装高校宣讲会管理系统软件来发挥其高效地信息…

C# Web控件与数据感应之 Control 类

目录 关于数据感应 Control 类 范例运行环境 simpleDataListEx方法 设计 实现 调用示例 数据源 调用 小结 关于数据感应 数据感应也即数据捆绑&#xff0c;是一种动态的&#xff0c;Web控件与数据源之间的交互&#xff0c;诸如 ListControl 类类型控件&#xff0c;在…

pytest教程-35-钩子函数-pytest_unconfigure

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest_configure钩子函数的使用方法&#xff0c;本小节我们讲解一下pytest_unconfigure钩子函数的使用方法。 pytest_unconfigure(config) 是一个 pytest 钩子函数&#xff0c;它在 pytest 退…