预编译(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.rhkb.cn/news/145213.html

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

相关文章

【计算机网络】 基于TCP的简单通讯(客户端)

文章目录 流程伪代码代码实现加载库创建套接字连接服务端收发数据关闭套接字、卸载库 测试 流程伪代码 //1、加载库//2、创建套接字//3、连接服务端while(true){//4、发送数据//5、接收数据} //6、关闭套接字、卸载库代码实现 加载库 int err 0;WORD version MAKEWORD(2, 2…

React 全栈体系(十五)

第八章 React 扩展 一、setState 1. 代码 /* index.jsx */ import React, { Component } from reactexport default class Demo extends Component {state {count:0}add ()>{//对象式的setState/* //1.获取原来的count值const {count} this.state//2.更新状态this.set…

leetCode 188.买卖股票的最佳时机 IV 动态规划 + 状态压缩

给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。 注意:你不能同时参与多…

ElasticSearch - 基于 JavaRestClient 查询文档(match、精确、复合查询,以及排序、分页、高亮)

目录 一、基于 JavaRestClient 查询文档 1.1、查询 API 演示 1.1.1、查询基本框架 DSL 请求的对应格式 响应的解析 1.1.2、全文检索查询 1.1.3、精确查询 1.1.4、复合查询 1.1.5、排序和分页 1.1.6、高亮 一、基于 JavaRestClient 查询文档 1.1、查询 API 演示 1.1.…

FPGA 图像缩放 千兆网 UDP 网络视频传输,基于RTL8211 PHY实现,提供工程和QT上位机源码加技术支持

目录 1、前言版本更新说明免责声明 2、相关方案推荐UDP视频传输--无缩放FPGA图像缩放方案我这里已有的以太网方案 3、设计思路框架视频源选择ADV7611 解码芯片配置及采集动态彩条跨时钟FIFO图像缩放模块详解设计框图代码框图2种插值算法的整合与选择 UDP协议栈UDP视频数据组包U…

数据库存储引擎和数据类型详细介绍

目录 一、数据库存储引擎(了解)1.了解MySQL体系结构2.存储引擎(了解)2.1.存储引擎的介绍2.2.存储引擎分类2.3.如何选择引擎? 3.事务控制语言(TCL)事务的四个特性(ACID) 二、数据类型(了解)1.整型…

【Vue.js】使用Element中的Mock.js搭建首页导航左侧菜单---【超高级教学】

一,Mock.js 1.1 认识Mock.js Mock.js是一个用于前端开发中生成随机数据、模拟接口响应的 JavaScript 库。模拟数据的生成器,用来帮助前端调试开发、进行前后端的原型分离以及用来提高自动化测试效率 总结来说,Element中的Mock.js是一个用于…

龙迅LT9611UXC 2PORT MIPICSI/DSI转HDMI(2.0)转换器+音频,内置MCU

龙迅LT9611UXC 1.描述: LT9611UXC是一个高性能的MIPI DSI/CSI到HDMI2.0转换器。MIPI DSI/CSI输入具有可配置的单 端口或双端口,1高速时钟通道和1~4高速数据通道,最大2Gbps/通道,可支持高达16Gbps的总带 宽。LT9611UXC支持突发…

7、Docker网络

docker网络模式能干嘛? 容器间的互联和通信以及端口映射 容器IP变动时候可以通过服务名直接网络通信而不受到影响 docker 网络模式采用的是桥接模式,当我们创建了一个容器后docker网络就会帮我们创建一个虚拟网卡,这个虚拟网卡和我们的容器网…

Appium混合页面点击方法tap的使用

原生应用开发,是在Android、IOS等移动平台上利用官方提供的开发语言、开发类库、开发工具进行App开发;HTML5(h5)应用开发,是利用Web技术进行的App开发。目前,市面上很多app都是原生和h5混合开发&#xff0c…

C理解(四):链表

本文主要探讨单链表与双链表相关知识。 linux内核链表(include/linux/list.h) 内核链表中纯链表封装,纯链表的各种操作函数(节点创建、插入、删除、遍历),纯链表内嵌在驱动结构体中,实现驱动的创建、插入、删除、遍历等 单链表 单链表链表头插…

FileManager/本地文件增删改查, Cache/图像缓存处理 的操作

1. FileManager 本地文件管理器,增删改查文件 1.1 实现 // 本地文件管理器 class LocalFileManager{// 单例模式static let instance LocalFileManager()let folderName "MyApp_Images"init() {createFolderIfNeeded()}// 创建特定应用的文件夹func cr…

【Java 进阶篇】MySQL 数据控制语言(DCL):管理用户权限

MySQL 是一个强大的关系型数据库管理系统,提供了丰富的功能和选项来管理数据库和用户。数据库管理员(DBA)通常使用数据控制语言(Data Control Language,简称 DCL)来管理用户的权限和访问。 本文将详细介绍…

uni-app 之 短信验证码登录

uni-app 之 短信验证码登录 image.png image.png <template><view style"width: 100%; display: flex; flex-direction:column; align-items:center;"><view style"width: 300px; margin-top: 100px;"><!-- // --><!-- 1&#…

JUnit介绍

JUnit是用于编写和运行可重复的自动化测试的开源测试框架&#xff0c; 这样可以保证我们的代码按预期工作。JUnit可广泛用于工业和作为支架(从命令行)或IDE(如Eclipse)内单独的Java程序。 JUnit提供&#xff1a; 断言测试预期结果。 测试功能共享通用的测试数据。 测试套件轻…

新闻报道的未来:自动化新闻生成与爬虫技术

概述 自动化新闻生成是一种利用自然语言处理和机器学习技术&#xff0c;从结构化数据中提取信息并生成新闻文章的方法。它可以实现大规模、高效、多样的新闻内容生产。然而&#xff0c;要实现自动化新闻生成&#xff0c;首先需要获取可靠的数据源。这就需要使用爬虫技术&#…

【AI视野·今日NLP 自然语言处理论文速览 第四十四期】Fri, 29 Sep 2023

AI视野今日CS.NLP 自然语言处理论文速览 Fri, 29 Sep 2023 Totally 45 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers MindShift: Leveraging Large Language Models for Mental-States-Based Problematic Smartphone Use Interve…

Source Insight 工具栏图标功能介绍

这篇文章并不介绍 Source Insight 的具体使用方法&#xff0c;这类教程网上有很多&#xff0c;这里只分析 Souce Insight 工具栏图标的功能。 文章目录 Source Insight 简介Souce Insight 工具栏文件操作新建&#xff08;CtrlN&#xff09;打开&#xff08;CtrlO&#xff09;保…

自学WEB后端02-基于Express框架完成一个交互留言板!

提示&#xff1a; 浏览器V8是JavaScript的前端运行环境 Node.js 是JavaScript 的后端运行环境 Node.js 中无法调用 DOM 和 BOM等浏览器内置 API 这个作业案例包含2部分内容&#xff0c; 第一部分是前端 前端完成界面内容CSS框架 第二部分是后端 完成用户留言存储&#xf…

overleaf杂谈-Springer文献格式问题

目录 overleaf写作问题记录1.Latex中的%问题&#xff08;文本变成灰色&#xff09;2.Springer文献格式问题2.1 新建reference.bib2.2 谷歌学术搜索文章并引用2.3 复制BibTex2.4 复制进reference.bib2.5 在sn-article.tex的\end{document}前添加语句2.6 引用文献2.7 Springer模板…