预编译(1)

目录

预定义符号:

使用:

结果:

预编译前后对比:

#define定义常量:

基本语法:

举例1:

结果:

预编译前后对比:

 举例2:

预编译前后对比:

 注意事项:

#define定义宏:

下⾯是宏的申明⽅式:

举例:

使用:

 预编译前后对比:

 结果:

 注意事项:

1.参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

举例:

正确的:

错误的:

2.不够严谨的写法导致的失误:

举例:

结果:

使用预编译进行查看替换的情况:

所以严谨的写法应该是:

带有副作用的宏:

 举例:

 当我们使用a++和b++这种带有永久性的效果时:

 预编译前后对比:​编辑

结果:

分析:

宏的替换规则:

宏和函数的对比:

优势:

函数和宏的过程:

劣势:

 宏做得到,函数做不到:

什么时候使用宏? 

总结图:



预定义符号:

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

__FILE__ //进⾏编译的源⽂件
__LINE__ //⽂件当前的⾏号
__DATE__ //⽂件被编译的⽇期
__TIME__ //⽂件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义

使用:

int main()
{ printf("%s\n", __FILE__);printf("%d\n", __LINE__);printf("%s\n", __DATE__ ;printf("%s\n", __TIME__);
}

结果:

预编译前后对比:

#define定义常量:

基本语法:

#define name stuff

举例1:

int main()
{int a = Mprintf("%d\n",M);printf("%s\n",STR);return 0 :
}
结果:

 

预编译前后对比:

 

 举例2:

#define forever for( ; ; )
int main()
{int a = M;printf("%d\n",M);printf("%s\n",STR);forever;return 0;
}

结果是无限循环

预编译前后对比:

 

 注意事项:

在define定义标识符的时候,不要在最后加上 ;  

#define定义宏:

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

下⾯是宏的申明⽅式:

 #define name( parament-list ) stuff

举例:

#define SOAURE(X) X*X

使用:

#define SOAURE(X) X*X
int main()
{int a = 5;printf("%d\n",SQUARE(a));return 0;
}
 预编译前后对比:

 结果:

 注意事项:

1.参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

举例:
正确的:
#define SQUARE(X) X*X
错误的:
#define SQUARE (X) X*X

错误在于SQUARE举例(x)之间有一个空格,正常来说是不因该又间隔的,这二者应该紧贴在一起,也就是换在原型上来说,name应该和 ( parament-list )紧贴着!

2.不够严谨的写法导致的失误:

举例:
#define SOAURE(X) X*X
int main()
{int a = 5;printf("%d\n",SQUARE(a+2));return 0;
}

按照我们的想法因,再进行#define替换后,应该是(a+2)*(a+2),因为a=5,所以最后的结果因该是7*7=49,但是结果并不是如此。

结果:

使用预编译进行查看替换的情况:

并不是我们想象中的 (a+2)*(a+2),而是a+2*a+2,也就是5+2*5+2=5+10+2=17

所以严谨的写法应该是:
#define SQUARE (X) ((X)*(X))

 以此避免因为不严谨而带来的错误。

带有副作用的宏:

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

副作用就是表达式求值的时候出现的永久性效果。 

 举例:

#define MAX(a, b) ((a)>(b)?(a):(b))

 当我们使用a++和b++这种带有永久性的效果时:

int main()
{int a = 15;int b = 9;int m = MAX(a++, b++);printf("%d\n",m);printf("a=%d b=%d\n",a,,b);return 0;
}

 预编译前后对比:

结果:

分析:

通过宏的替换,将   int m = MAX(a++, b++);替换为了int m = ((a++)>(b++)?(a++):(b++));

也正是因为替换,导致了++的不确定性。

  1. 1.因为a=15,再(a++)>(b++)中进行了使用,使用完后 a和b都因为++加上了1
  2. 2.因为再(a++)>(b++)中a比b大,所以执行?(a++):(b++));因为是a大,所以只执行(a++),所以最后答案输出给m的是16,但是输出完后,进行++执行,所以a又继续+1,得到17

宏的替换规则:

通过上诉我们得知了,宏是先把对应的内容替换掉,再进行运算。

而在程序中扩展#define定义符号和宏时,需要涉及⼏个步骤。

  1. 在调⽤宏时,⾸先对参数进⾏检查,看看是否包含任何由#define定义的符号。如果是,它们⾸先 被替换。
  2. 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏,参数名被他们的值所替换。  
  3. 最后,再次对结果⽂件进⾏扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上 述处理过程。

注意事项:

  1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归
  2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。 

如图所示:printf("M = %d\n",M);中的M =%d\n的M并未被替换 。

 

宏和函数的对比:

 

如上述代码所示:宏和函数,那一个更好呢?

其实,在函数和宏中,其实对于较为简单的代码来说,宏是一种更好的选择,也是宏的一种优势。

优势:

  • 函数是进行调用的,而宏是直接进行替换的,对于调用再编译器中是有一个过程的,而对于替换而言,这种替换的过程比调用要简单的多 
#define MAX(a,b) ((a)>(b)?(a):(b))
int m = MAX(100,101);
int m = ((100) > (101) ? (100) :(101));

再预编译中,上诉的两个代码其实是一个东西, 因为宏的作用下,编译器直接将第一行代码变成了第二行代码,或者说,直接把第一行代码看出第二行代码对待,这种方式是宏特有的替代。

也正是因为这种直接替代,使得宏在简单的代码中比调用函数的运行速度更快。

而调用函数:

 在使用调用函数正式调用之前,会执行一堆代码指令,当这一堆指令完成后才开始进行调用函数。

这也是宏比函数快的原因。

函数和宏的过程:

  • 宏因为是替换,所以宏的参数是没有类型的。 

这是比函数快的原因,也是一个不确定的因素。

劣势:

  • 因为每一次使用宏,就会有一段代码被宏替代,若设置的宏较长,那么整体的代码会被宏拉长,反倒是函数因为可以多次使用会更简洁。
  •  关于调试,当程序运行的时候,预处理就以及结束了,看到的宏的代码已经被预编译了,看不到预编译之前的宏,所以也可以说,宏不能逐步调试。
  • 其次最后,因为参数没有类型,所以不是非常稳定。
  • 以及,优先级问题,也就是之前设置的宏不够严谨导致参数的运算过程中符号优先级的出错。
#define SOAURE(X) X*X//不够严谨的写法

 宏做得到,函数做不到:

当我们开辟空间的时候,如果怕麻烦的花可以使用宏来定义。

int *p = (int*)malloc(10*sizeof(int));

使用宏后:

#define MALLOC(n,type) (typex)malloc(n*sizeof*(type))

利用了宏是可以把类型当参数进行传值替代的特点,这就是函数做不到的地方。 

#define MALLOC(n,type) (typex)malloc(n*sizeof*(type))
int main()
{int* p = MALLOC(10,int);return 0;
}

什么时候使用宏? 

  • 计算逻辑如果比较简单,就可以使用宏
  • 计算逻辑比较复杂,建议使用函数 

总结图:

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

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

相关文章

ELK介绍

一、前言 前面的章节我们介绍通过ES Client将数据同步到ElasticSearch中,但是像日志这种数据没有必要自己写代码同步到ES那样会折腾死,直接采用ELK方案就好,ELK是Elasticsearch、Logstash、Kibana三款开源软件的缩写,ELK主要用于…

【论文阅读】大语言模型中的文化道德规范知识

摘要: 在已有的研究中,我们知道英语语言模型中包含了类人的道德偏见,但从未有研究去检测语言模型对不同国家文化的道德差异。 我们分析了语言模型包含不同国家文化道德规范的程度,主要针对两个方面,其一是看语言模型…

beanstalkd 启动跟停止【经常使用 nohup 和 配合来启动程序,如: nohup ./test 同时免疫SIGINT和SIGHUP信号】

启动命令:  nohup /usr/bin/beanstalkd -l 0.0.0.0 -p 11300 & >> /dev/null 2>&1 正常启动后,利用 【lsof -i:11300】查看 该服务是否正常启动 停止命令: /etc/init.d/beanstalkd stop 正常停止后,利用 【l…

P2PNet-Soy原理梳理

前文总结了P2PNet源码以及P2PNet-Soy源码实现方法,相关链接如下: 人群计数P2PNet论文:[2107.12746] Rethinking Counting and Localization in Crowds:A Purely Point-Based Framework (arxiv.org) p2p人群计数源码:GitHub - Te…

Java Spring Boot 开发框架

Spring Boot是一种基于Java编程语言的开发框架,它的目标是简化Java应用程序的开发过程。Spring Boot提供了一种快速、易于使用的方式来创建独立的、生产级别的Java应用程序。本文将介绍Spring Boot的特性、优势以及如何使用它来开发高效、可靠的应用程序。 一、简介…

云服务器租用价格表概览_阿里云腾讯云华为云

云服务器租用价格多少钱一年?阿腾云分享阿里云、腾讯云和华为云的云服务器租用价格表:阿里云2核2G服务器108元一年起、腾讯云2核2G3M带宽轻量服务器95元一年、华为云2核2G3M云耀L实例89元一年起,阿腾云分享更多关于云服务器租用价格明细&…

Kubernetes基础(五)-Service

1 引言 Service 主要用于提供网络服务,通过Servicel的定义,能够 为客户端应用提供稳定的访问地址(域名或IP地址)和负载均衡功能,以及屏蔽后端Endpoint的变化,是Kubernetes实现微服务的核心资源。 本文详细…

博弈论中静态博弈经典场景案例

博弈论中静态博弈经典场景案例 1、齐威王田忌赛马 田忌赛马是中国家喻户晓的故事,故事讲述的是齐国大将田忌的谋士孙膑如何运用计谋帮助田忌在与齐威王赛马时以弱胜强的故事,这个故事其实本质也是一个博弈的过程。     齐威王要和田忌赛马&#xff…

二叉树MFC实现

设有一颗二叉树如下; 这似乎是一颗经常用作示例的二叉树; 对树进行遍历的结果是, 先序为:3、2、2、3、8、6、5、4, 中序为:2、2、3、3、4、5、6、8, 后序为2、3、2、4、5、6、8、3&#xff1b…

MySQL学习笔记25

逻辑备份 物理备份 在线热备: 真实案例: 数据库架构是一主两从,但是两台从数据库和主数据不同步。但是每天会全库备份主服务器上的数据到从服务器上。需要解决主从不同步的问题。 案例背后的核心技术: 1、熟悉MySQL数据库常见…

【Java】面向过程和面向对象思想||对象和类

1.面向过程和面向对象思想 两者都贯穿于软件分析、设计和开发的各个阶段,对应面向对象就分别称为面向对象的分析(OOA)、面向对象的设计(OOD)和面向对象的编程(OOP)。C语言是一种典型的面向过程语…

【计算机视觉|人脸建模】PanoHead:360度几何感知的3D全头合成

本系列博文为深度学习/计算机视觉论文笔记,转载请注明出处 标题:PanoHead: Geometry-Aware 3D Full-Head Synthesis in 360 ∘ ^{\circ} ∘ 链接:[2303.13071] PanoHead: Geometry-Aware 3D Full-Head Synthesis in 360 ∘ ^{\circ} ∘ (arx…

python reportlab生成pdf

这里自定义了pagetemplate,使用BaseDocTemplate,但我感觉一般使用SimpleDocTemplate就可以。 from reportlab.platypus import Frame from reportlab.lib.pagesizes import A4, landscapepadding dict(leftPadding72,rightPadding72,topPadding72,bott…

Java面经整理(2)

一)为什么要使用克隆? 实现原型设计模式,实现备份和恢复 假设此时这个系统是支持用户进行配置的,是支持用户设置皮肤的颜色,设置系统的快捷键,此时就需要使用原型设计模式,不能自己设置把别人的设置都给改了&#xff…

大数据Doris(三):Doris编译部署篇

文章目录 Doris编译部署篇 一、Doris编译

学信息系统项目管理师第4版系列13_立项管理

1. 项目立项管理包括 1.1. 项目建议与立项申请 1.2. 项目可行性研究 1.2.1. 初步可行性研究 1.2.2. 详细可行性研究 1.2.2.1. 不可缺少 1.2.2.1.1. 【高21上选21】 1.2.3. 可以依据项目的规模和繁简程度合二为一 1.3. 项目评估与决策 2. 立项申请 2.1. 项目建议书 2…

rust学习-Arc

背景介绍 线程安全的引用计数指针。 “Arc”代表“原子引用计数 Atomically Reference Counted”。 Arc 类型提供在堆中分配的 T 类型值的共享所有权(shared ownership)。在 Arc 上调用克隆会生成一个新的 Arc 实例,该实例指向堆上与源 Arc 相同的分配,同时增加引用计数。…

Lua语法之简单变量

--nil有点类似空null a nil print(a) --type函数得到类型 返回值是string print(type(a)) print("*****")--number是数值 int float这些 --lua的变量可以随便赋值 自动识别类型 a 1 print(a) print(type(a)) print("*****")--siting可以用单引号双引号 a…

LeetCode 面试题 08.01. 三步问题

文章目录 一、题目二、Java 题解 一、题目 三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。 示例1: 输入&…