预处理详解(完结篇)

news2024/12/27 5:35:49
⽬录
一. 什么是预处理
c
    1  预定义符号
    2. #define定义常量
    3 . #define定义宏
    4. 带有副作⽤的宏参数
    5. 宏替换的规则
    6 宏函数的对⽐
#和##
四  命名约定
五. #undef

一      什么是预处理

有许多文件中都内容我们是看不懂的,那怎么才能令我们看得懂呢?其实我们的系统拿到各种文件后,要进行一系列的操作过程,才能将文件转化成我们能够看得懂的信息。而翻译环境能够把源代码转换成可执行的机器指令,这个过程主要有编译和链接两个过程组成。

编译链接的过程:

预处理就是编译和链接过程中的一个阶段。在预处理阶段,源文件和头文件都会被处理成.i为后缀的文件,接下来我将详解预处理相关知识。

  预处理指令

1   预定义符号
C语⾔设置了⼀些预定义符号,可以直接使⽤预定义符号也是在预处理期间处理的

预定义符号:

1     __FILE__ //进⾏编译的源⽂件
2     __LINE__ //⽂件当前的⾏号
3     __DATE__ //⽂件被编译的⽇期
4     __TIME__ //⽂件被编译的时间
5     __STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
注意:需要注意的是,__STDC__无法在vs中使用。

举个例子:

输出结果:

 

#define 定义常量 
2.1 基本语法:这里 需要特别注意的是,define定义常量时不要再define行末尾加上“;”,如果 加上 “;” 这样容易导致问题。
# define name stuff
                 |         |
             名字      内容

2.2  举个例⼦: 

 

3     #define定义宏

3.1  概念:#define机制包括了⼀个规定,允许把参数替换到⽂本中,这种实现通常称为宏(macro)或定义宏(define macro)。

那我们来段代码感受一下宏/定义宏

  我们定义了一个宏ADD(a,b),又因为允许把参数替换到⽂本中,所以这个宏ADD(a,b)可以计算传入宏的两个参数a,b的和

3.2   宏的申明⽅式:

# define name( parament-list ) stuff
其中的 parament-list 是⼀个由逗号隔开的符号表,它们可能出现在stuff中
注意:参数列表的左括号必须与name紧邻,如果两者之间有任何空⽩存在,参数列表就会被解释为stuff的 ⼀部分。

3.3  使用宏/定义宏可能遇见的错误

示例:

输出结果:

从输出结果上来看,这个结果很令人出乎意料我们想得到6*7的结果,那为什么结果是13呢?这就要了解一下宏/定义宏的运行原理:其实,我们在把两个参数传过去后,(其原理是将参数在不计算的情况下直接传参传参过去)计算的其实是5+1*3+1,根据运算法则,,先算乘法再算加法,所以输出结果为13。

那怎么解决呢?我们想如果先计算a+1,在计算b+1,再计算a+1的结果*b+1的结果,这时候我们用括号把a+1括起来先计算不就可以吗?

这时候我们就得到我们想要的结果了。

总结:所以⽤于对数值表达式进⾏求值的宏定义都应该⽤这种⽅式加上括号,避免在使⽤宏时由于参数中的操作符或邻近操作符之间不可预料的相互作⽤。
   带有副作⽤的宏参数

4.2什么是副作⽤

假如我们想令一个数+1,但是这个数自己的大小不能变:

1   x+ 1 ; //不带副作⽤  x未变
2    x++;//带有副作⽤  x变大了

4.2带来的后果:宏参数在宏的定义中出现超过⼀次的时候,如果参数带有副作⽤,那么你在使⽤这个宏的时候就可能出现危险,导致不可预测的后果

示例:

输出结果:

我们来分析一下为什么会出现这个结果,首先我们(利用宏原理)先把宏的值全都替换掉得到:

printf("%d %d %d",a,b, ((a++) > (b++) ? (a++) : (b++)));

再分析其过程: a++的特性是先用后加,所以我们实际比较的是4和5两个数。再比较完之后,a变成5,b变成6。然后返回值为b++。同样是先用后加,所以先返回b的值6,然后再自增1,现在b等于7。

5    宏替换的规则
在程序中扩展#define定义符号和宏时,需要涉及⼏个步骤。
1. 在调⽤宏时,⾸先对参数进⾏检查,看看是否包含任何由#define定义的符号。如果是,它们⾸先被替换。
2. 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果⽂件进⾏扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
       2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
6    宏函数的对⽐

我们看到这应该已经能发现了,宏和函数功能及其相似,那他们两个到底谁更好呢

和函数相⽐宏的优势:
1. ⽤于调⽤函数和从函数返回的代码可能⽐实际执⾏这个⼩型计算⼯作所需要的时间更多。所以宏⽐函数在程序的规模和速度⽅⾯更胜⼀筹。
2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使⽤。反之这个宏怎可以适⽤于整形、⻓整型、浮点型等可以⽤于 > 来⽐较的类型。宏的参数是类型⽆关 的。
和函数相⽐宏的劣势:
1. 每次使⽤宏的时候,⼀份宏定义的代码将插⼊到程序中。除⾮宏⽐较短,否则可能⼤幅度增加程序的⻓度。
2. 宏是没法调试的。
3. 宏由于类型⽆关,也就不够严谨。
4. 宏可能会带来运算符优先级的问题,导致程容易出现错。
所以如果是简单的问题例如: 在两个数中找出较⼤的⼀个时, 写成宏,更有优势⼀些 如果是复杂的问题,使用函数更有优势
两者多个方面对比:

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

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

相关文章

【k8s】监控metrics-server

metrics-server介绍 Metrics Server是一个集群范围的资源使用情况的数据聚合器。作为一个应用部署在集群中。Metric server从每个节点上KubeletAPI收集指标,通过Kubernetes聚合器注册在Master APIServer中。为集群提供Node、Pods资源利用率指标。 就像Linux 系统一样…

ERP 入库生产第一个版本完成

剩下的逻辑都是基于入库表达操作,资源划分,在销售,出库 windows 下直接部署 mysql 数据库,更轻量一些

ESP32-S3模组上跑通ES8388(12)

接前一篇文章:ESP32-S3模组上跑通ES8388(11) 二、利用ESP-ADF操作ES8388 2. 详细解析 上一回解析了es8388_init函数中的第5段代码,本回继续往下解析。为了便于理解和回顾,再次贴出es8388_init函数源码,在…

对于相对速度的重新理解 - 15

换一个视角看待能量可能一切都更为清晰,从, 可以意识到,最终质量 指的是 的数量。这个数量就是 我们可以去除电性振动和磁性振动的影响,把这两种振动的影响归结在 里面,这就像是,有一百万个某种物体&#x…

记一次腾讯云海外服务器http能正常访问https访问拒绝问题处理过程

最近双十一, 购了一台腾讯云的海外服务器, 开通后就是一堆的服务器软件安装数据上传和配置,没想到,等待配置完成后才发现https无法正常访问,于是开启了自查。 1. 检查nginx软件的ssl配置 nginx http https配置参考 server {l…

Java 单元测试模拟框架-Mockito 的介绍

Mockito 是什么 Mockito 是一个用于单元测试的模拟框架,基于它可以使用简洁易用的API编写出色的测试。 Mockito 允许开发人员创建和管理模拟对象(mock objects),以便在测试过程中替换那些不容易构造或获取的对象。 Mockito的基本…

分享一款 Vue 图片编辑插件 (推荐)

💥本篇文章给大家分享一款强大到没朋友的Vue图片编辑插件,可以对图片进行旋转、缩放、裁剪、涂鸦、标注、添加文本等,快来试试并收藏吧!💕 这是一款对图片进行旋转、缩放、裁剪、涂鸦、标注、添加文本在线处理的图片处…

在基于控制器的 API 和最小 API 之间进行选择

文章目录 ASP.NET Core 支持两种创建 API 的方法:基于控制器的方法和最小 API。 API 项目中的控制器是派生自 ControllerBase 的类。 最小 API 在 Lambda 或方法中使用逻辑处理程序定义终结点。 本文解释了这两种方法之间的差异。 最小 API 的设计默认隐藏了主机类…

【UVM】phase机制

Phase的种类 funcion phase 不消耗仿真时间 八种(图中白色背景) task phase 消耗仿真时间 一种(图中灰色背景),run_phase又可以细分为十二种 Phase的功能 bulid_phase:uvm_component类的实例化&…

《装甲车内气体检测“神器”:上海松柏 K-5S 电化学传感器模组详解》

《装甲车内气体检测“神器”:上海松柏 K-5S 电化学传感器模组详解》 一、引言二、K-5S 电化学传感器模组概述(一)产品简介(二)产品特点(三)产品适用场景 三、电化学传感器原理及优点(一&#xf…

【Linux课程学习】:文件第二弹---理解一切皆文件,缓存区

🎁个人主页:我们的五年 🔍系列专栏:Linux课程学习 🌷追光的人,终会万丈光芒 🎉欢迎大家点赞👍评论📝收藏⭐文章 Linux学习笔记: https://blog.csdn.net/d…

汽车控制软件下载移动管家手机控车一键启动app

移动管家手机控制汽车系统是一款实现车辆远程智能控制的应用程序‌。通过下载并安装特定的APP,用户可以轻松实现以下功能:‌远程启动与熄火‌:无论身处何地,只要有网络,即可远程启动或熄火车辆,提前预冷或预…

匿名发帖/匿名论坛功能设计与实现(编辑发帖部分)

前言 还是之前的音乐系统,首页一直是没想好写些什么,想写一个基于数据分析筛选的歌曲推荐功能,但是目前技术选型没太有考究等以后再实现吧,昨天突然想到可以把首页设计成前40%页面是歌曲推荐后面接下来就是一段匿名论坛功能&…

微信小程序 城市点击后跳转 并首页显示被点击城市

在微信小程序中,渲染出城市列表后,如何点击城市,就跳转回到首页,并在首页显示所点击的城市呢? 目录 一、定义点击城市的事件 二、首页的处理 首页:点击成都市会跳转到城市列表 城市列表:点击…

DOM,事件监听和VUE入门

四个事件、 JS模块化 VUE入门 Ajax VUE总结

Python学习笔记(5)Python的创建型设计模式

创建型设计模式(Creational Design Patterns),主要关注对象的创建机制。这类模式可以使得系统更加独立于如何创建、组合和表示其对象。通过将这些职责分离出来,创建型设计模式有助于提高代码的灵活性和复用性。 本书的范例代码已经…

云备份实战项目

文章目录 前言一、整体项目简介二、服务端环境及功能简介三、 客户端环境及功能简介四、服务端文件管理类的实现1. 获取文件大小,最后一次修改时间,最后一次访问时间,文件名称,以及文件内容的读写等功能2. 判断文件是否存在&#…

Java有关数组的相关问题

Java中的栈和堆的含义 栈 存储局部变量:栈主要用于存储方法中的局部变量,包括基本数据类型(int、double、boolean等)和对象的引用(不包含对象本身)。 遵循后进先出原则:当一个方法被调用时&…

使用Dify与BGE-M3搭建RAG(检索增强生成)应用-改进一,使用工作流代替Agnet

文章目录 前言Agent vs 工作流编写工作流 前言 在上一篇中,我们实现了一个基本的基于Dify的RAG的示范。 使用Dify与BGE-M3搭建RAG(检索增强生成)应用 这个效果确实很差。 我们一起来看看,该怎么改进。 今天我们就尝试一下&…

Python语法基础(四)

🌈个人主页:羽晨同学 💫个人格言:“成为自己未来的主人~” 高阶函数之map 高阶函数就是说,A函数作为B函数的参数,B函数就是高阶函数 map:映射 map(func,iterable) 这个是map的基本语法,…