C语言中宏和函数的9个区别,你都了解吗?

C语言中的宏和函数是非常相似的,它们都可以完成类似的功能。比如,想要求2个数的较大值,使用宏的写法是:

// 宏的定义
#define MAX(x, y) ((x)>(y)?(x):(y))

// 使用
int m = MAX(10, 20);

使用函数的写法是:

// 函数的定义
int Max(int x, int y)
{
	return x>y ? x : y;
}

// 使用
int m = Max(10, 20);

既然宏和函数长的那么像,究竟什么时候用宏,什么时候用函数呢?这就要了解一下它们之间的区别了。我总结了他俩之间的区别,主要体现在以下几点:

  1. 代码长度。
  2. 执行速度。
  3. 操作符优先级。
  4. 带有副作用的参数。
  5. 参数类型。
  6. 调试。
  7. 递归。
  8. 命名约定。
  9. 其他。

宏和函数的区别

1.代码长度

宏会在每个使用它的地方替换。比如前面提到的求两个数的较大值的宏,假设这么使用:

int m = MAX(10, 20);
// ...
m = MAX(20, 30)
// ...
m = MAX(30, 40);
// ...
// ...

每个使用宏的地方都会被替换掉。

int m = ((10)>(20)?(10):(20));
// ...
m = ((20)>(30)?(20):(30))
// ...
m = ((30)>(40)?(30):(40));
// ...
// ...

这里的宏体比较短,所以替换进去后,代码的长度并没有明显的提升。但是,假设这个宏有100行代码,每个地方展开后,展开3次,就会多出300行代码。如果更加频繁的调用,调用100次,就会多出10000行代码。所以,当宏体比较长,尤其是调用次数还比较多的情况下,会导致代码长度大大增加。

而函数就不存在这个问题,函数不管调用几次,都只需要写一次函数的代码,每次使用时直接调用即可,代码长度是可控的。

2.执行速度

函数调用时,需要在栈空间上开辟一块栈帧,参数还要压栈。当函数体的代码执行完后,需要返回时,还要销毁栈帧。这些都是有开销的,执行速度较慢。

但是,宏的代码在预处理阶段就已经完成替换,不存在这个问题,执行速度较快。

3.操作符优先级

使用宏时,代码是在对应的位置直接展开,如果该位置周围有其他操作符,有可能干扰宏体内的操作符的执行顺序,导致错误的结果。比如:

#define DOUBLE(x) x+x

如果这么调用:

int ret = 2 * DOUBLE(10);

我们想的是:DOUBLE(10)会算出20,再乘2,得到40。然而,实际代码会这样展开:

int ret = 2 * 10+10;

由于乘号的优先级比较高,会先算2*10,得到20,再加10得到30,和预期的结果不一致。

但函数不存在这个问题。

int Double(int x)
{
	return x+x;
}

当这样调用时:

int ret = 2 * Double(10);

一定是先把10传给函数,函数计算完后返回20,再进行别的计算。

当然,如果参数本身是表达式时,也会有相同的问题。比如:

#define SQUARE(x) x*x

int Square(int x)
{
    return x*x;
}

int ret1 = SQUARE(3 + 2);
int ret2 = Square(3 + 2);

函数就是正常的,先计算3+2得到5,在把5传参,得到25。但是宏会这样替换:

int ret1 = 3 + 2*3 + 2;

由于乘号的优先级较高,得到的结果就是11,和预期结果不符。

为了解决这样的问题,建议写宏时多加括号,防止受到其他操作符的影响。比如:

#define DOUBLE(x) ((x)+(x))
#define SQUARE(x) ((x)*(x))

4.带有副作用的参数

对于MAX宏,如果这样使用:

int x = 3;
int y = 5;
int m = MAX(x++, y++);

我们的想法是:把x和y传参,算出x和y的较大值为5,即m应该是5,而后置++会把x和y的值分别改成4和6。但是实际替换时是这么替换的:

int m = ((x++)>(y++)?(x++):(y++));

计算时,先判断x++>y++这个表达式,显然x<y,故为假,判断完后x和y都要++,x改成4,y改成6,返回y++的结果,即6,y再++改成7。所以最终结果是:m为6,x为4,y为7,和预期不符,原因是带有副作用的宏参数影响了结果。

函数就不存在这个问题。如果调用Max函数:

int x = 3;
int y = 5;
int m = Max(x++, y++);

由于函数的传参和函数体代码的执行是分开的,所以结果和预期相同,m=5, x=4, y=6。

5.参数类型

宏是直接对代码进行文本替换,是不检查类型的。比如:

int m1 = MAX(3, 5);// 会被替换成int m1 = ((3)>(5)?(3):(5));
double m2 = MAX(3.2, 5,3); // 会被替换成double m2 = ((3.2)>(5.3)?(3.2):(5.3));

但是函数的形参是有类型的,传参时会对类型进行检查。比如前面的Max函数,参数列表是(int, int),只能求2个整数的较大值,如果要求两个浮点数的较大值,是无能为力的。

6.调试

宏直接完成代码的替换,不方便调试。因为宏的替换在预处理阶段已经完成,但是调试时调试的是最终生成的可执行程序。由于已经完成了替换,看到的代码和调试的代码是不一样的。函数就没有这个问题,可以逐语句调试。

7.递归

宏不能递归,函数可以实现递归。

8.命名约定

一般宏的名字为全大写,函数名不会全大写。比如:

  1. 宏名:MAX, DOUBLE, SQUARE。
  2. 函数名:Max, Double, Square。

但是也有例外。比如库中的offsetof是一个宏,而不是函数。

9.其他

宏可以实现直接把参数转换成字符串,或者把两个标识符连起来。具体可以看我之前写的这篇博客:你知道C语言中的#和##分别是什么意思吗?

总结

  1. 宏的代码长度较长,因为会被多次替换。函数只存一份代码,长度较短。
  2. 函数调用和返回都有开销,速度较慢。宏速度较快。
  3. 宏会受到操作符优先级的影响,导致结果可能和预期不符,函数没有这个问题。解决方案:写宏时,最好多加括号来限定操作符的执行顺序。
  4. 宏有可能受到带有副作用的参数的影响,函数无此影响。
  5. 函数有参数类型检查,宏没有。
  6. 宏不方便调试,函数无此问题。
  7. 函数可以递归,宏不可以。
  8. 宏名一般全大写,函数一般不会全大写。
  9. 宏可以实现一些函数实现不了的操作,比如把参数直接转换成字符串,连接2个标识符等。

根据以上几点,一般来说,一些简单的逻辑可以使用宏实现,比如求2个数的较大值。但是当代码长度比较长,或者逻辑比较复杂时,建议使用函数实现。

感谢大家的阅读!

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

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

相关文章

[Golang从零到壹] 1.环境搭建和第三方包管理

文章目录安装go环境go.mod第一种情况&#xff0c;选择GOPATH第二种情况&#xff0c;不选择GOPATH(推荐)GO111MODULEgo module可执行文件位置安装go环境 go在安装时选择好安装目录完成安装之后&#xff0c;还需要设置两个环境变量&#xff1a;GOROOT、GOPATH GOROOT即go的安装…

UnQLite入门

本文介绍UnQLite的基本使用&#xff0c;包括增删改查&#xff0c;事务ACID 文章目录UnQLite介绍UnQLite常用接口函数返回码DemoKey/Value存储数据库游标UnQLite介绍 UnQLite简介 UnQLite是&#xff0c;由 Symisc Systems公司出品的一个嵌入式C语言软件库&#xff0c;它实现了一…

Scrapy-核心架构

在之前的文章中&#xff0c;我们已经学习了如何使用Scrapy框架来编写爬虫项目&#xff0c;那么具体Scrapy框架中底层是如何架构的呢&#xff1f;Scrapy主要拥有哪些组件&#xff0c;爬虫具体的实现过程又是怎么样的呢&#xff1f; 为了更深入的了解Scrapy的相关只是&#xff0…

Chatgpt 指令收集

在使用 ChatGPT 时&#xff0c;当你给的指令越精确&#xff0c;它的回答会越到位&#xff0c;举例来说&#xff0c;假如你要请它帮忙写文案&#xff0c;如果没给予指定情境与对象&#xff0c;它会不知道该如何回答的更加准确。 一、写报告 1、我现在正在 [报告的情境与目的]。…

低代码平台应该具备哪些能力?

什么样的低代码无代码平台才算好的平台呢&#xff0c;Gartner 共列出了低代码平台的11个关键能力维度&#xff1a; 1、易用性。易用性是标识低代码平台生产力的关键指标&#xff0c;是指在不写代码的情况下能够完成的功能的多少。 2、用户体验。一般来说&#xff0c;独立软件开…

2023Q2押题,华为OD机试用Python实现 -【机智的外卖员】

最近更新的博客 华为 od 2023 | 什么是华为 od,od 薪资待遇,od 机试题清单华为 OD 机试真题大全,用 Python 解华为机试题 | 机试宝典【华为 OD 机试】全流程解析+经验分享,题型分享,防作弊指南华为 od 机试,独家整理 已参加机试人员的实战技巧本篇题解:机智的外卖员 题目…

Java中的死锁

1.什么是死锁 死锁&#xff1a;多个线程同时被阻塞&#xff0c;它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期的阻塞&#xff0c;线程不可能正常终止。 【举个栗子】滑稽老铁和女生去吃饺子。吃饺子需要醋和饺子。 滑稽老哥抄起了酱油瓶&#xff0c;女生抄起…

【技术教程】在EasyCVR平台中打开第三方桌面端应用的实现过程

EasyCVR视频融合平台基于云边端协同架构&#xff0c;具有强大的数据接入、处理及分发能力&#xff0c;平台支持海量视频汇聚管理&#xff0c;可支持多协议接入&#xff0c;包括市场主流标准协议与厂家私有协议及SDK&#xff0c;如&#xff1a;国标GB28181、RTMP、RTSP/Onvif、海…

vue 引入高德地图当前定位失败 Get ipLocation failed.Geolocation permission denied.

getCurrentPosition 返回的 message 原因解析 &#xff1a; Get ipLocation failed&#xff1a;IP 精确定位失败&#xff0c;精确IP定位服务目前无法完全覆盖所有用户 IP&#xff0c;失败率在5%左右。sdk 定位失败&#xff1a;检查 sdk的 key 是否设置好&#xff0c;以及 webv…

如何远程连接SQLServer数据库

如何远程连接SQLServer数据库 准备工作 1.打开 选中如下的连接方式 连接成功后就会出出现 2.连接成功后&#xff1a;右键设置属性 安全性设置&#xff1a;如下图所示 设置连接属性&#xff1a; 设置完成之后点击完成&#xff01;&#xff01;&#xff01; 3.打开 启动sqlSe…

华为OD机试用JS实现 -【查找树中的元素 or 查找二叉树节点】(2023-Q2 押题)

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:查找树中的元素 or 查找二叉树…

2023年南京晓庄学院五年一贯制专转本食品科学与工程专业考试大纲

2023年南京晓庄学院五年一贯制专转本食品科学与工程专业考试大纲 专业科目一 &#xff1a;微生物学基础 【参考书目】《食品微生物学》&#xff0c;杨玉红主编&#xff0c; 中国质检 出版社/中国标准出版社&#xff0c;2017(十三五高职高专院校规划教材) 【考试大纲】 ( 一) 考…

jsp054ssm高校学生成绩管理系统hsg421010A5程序

系统主要包含了学生信息管理、成绩信息管理等多个功能模块。下面分别简单阐述一下这几个功能模块需求。不同的权限对应相应的功能模块的需求&#xff0c;管理员权限的级别是最高的&#xff0c;所以所对应的需求是最多的&#xff0c;下面根据不同的权限分别简单阐述一下各个权限…

神奇智能搜索引擎:perplexity智能搜索引擎(ChatGPT与Edge合体——联网版chatGPT)

目录前言一、Perplexity AI网站介绍二、优点介绍2-0、界面介绍2-1、纯净、时效性、来源说明2-2、基于AI对话形式的搜索引擎三、使用方法介绍总结前言 ChatGPT背后的语言大模型OpenAI GPT 3.5&#xff0c;和微软的必应检索系统整合在一起&#xff1b;同时吸取这二者的长处&#…

Python爬虫|西安地铁客流人次获取与分析(一)

一、目标 通过Python编写爬虫程序,爬取西安地铁发布的客流数据,并保存到CSV文件中,并进行数据可视化与分析。 本次使用的库包括:requests、BeautifulSoup、xpath、csv、json、Pycharts等。 二、爬取思路分析 1、WB网页版 打开XLWB主页网址。 图1.新浪微博手机端网址 …

智慧水利整体解决方案2022(ppt可编辑)

业务场景与痛点分析 聚焦场景&#xff1a; 水工监控、水利监管、城市治水、基层防汛业务场景。 痛点分析&#xff1a; 水工监控中&#xff0c;全国水库共有9.8万座&#xff0c;其中超过50%处于病险状态&#xff1b; 水利监管中&#xff0c;运行管理薄弱、设备老化、预报预警不…

弱监督语义分割的token对比笔记

Token Contrast for Weakly-Supervised Semantic Segmentation 摘要 目的&#xff1a; 使用图像级标签的弱监督语义分割(WSSS)通常利用类激活映射(CAM)生成伪标签。受CNN局部结构感知的限制&#xff0c;CAM通常不能识别整体目标区域。虽然最近的视觉转换器(ViT)可以弥补这一…

汇编实验4(99乘法表,整数分解,素数环,迷宫问题)【栈传参,递归,寻址方式】

目录 一、99乘法表 汇编代码 效果 二、整数拆分 问题描述 c代码 汇编代码 效果 三、素数环 问题描述 c代码 效果 四、迷宫问题 问题描述 c代码 汇编代码 效果 一、99乘法表 汇编代码 INCLUDE Irvine32.inc .data a db *,0 .code main PROC mov ebx,1;ebxi m…

给对象新增两个数组,并分别给对象名称、名字

给对象新增两个数组&#xff0c;并分别给对象名称 代码如下&#xff1a; let a {}a.shuzu [1, 2, 3]a.zhangzu [5, 6, 7]console.log(测试添加, a)console.log(打印后台传递的值, this.res)

ToBeWritten之物联网MQTT、Z-Wave等协议

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…