我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C语言中宏和函数的9个区别详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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.命名约定

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

宏名:MAX, DOUBLE, SQUARE。

函数名:Max, Double, Square。

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

9.其他

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

总结

1.宏的代码长度较长,因为会被多次替换。函数只存一份代码,长度较短。

2.函数调用和返回都有开销,速度较慢。宏速度较快。

3.宏会受到操作符优先级的影响,导致结果可能和预期不符,函数没有这个问题。解决方案:写宏时,最好多加括号来限定操作符的执行顺序。

4.宏有可能受到带有副作用的参数的影响,函数无此影响。

5.函数有参数类型检查,宏没有。

6.宏不方便调试,函数无此问题。

7.函数可以递归,宏不可以。

8.宏名一般全大写,函数一般不会全大写。

9.宏可以实现一些函数实现不了的操作,比如把参数直接转换成字符串,连接2个标识符等。

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

以上就是C语言中宏和函数的9个区别详解的详细内容,更多关于C语言宏 函数的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C语言中宏和函数的9个区别详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C语言中宏和函数的9个区别详解

C语言中的宏和函数是非常相似的,它们都可以完成类似的功能。本文为大家整理了C语言中宏和函数的9个区别,感兴趣的小伙伴可以跟随小编一起了解一下
2023-05-14

C语言中宏和函数的区别有哪些

这篇文章主要介绍了C语言中宏和函数的区别有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C语言中宏和函数的区别有哪些文章都会有所收获,下面我们一起来看看吧。C语言中的宏和函数是非常相似的,它们都可以完成类似
2023-07-05

详解C语言中函数宏的三种封装方式

函数宏,即包含多条语句的宏定义,其通常为某一被频繁调用的功能的语句封装,且不想通过函数方式封装来降低额外的弹栈压栈开销。本文就来聊聊函数宏的三种封装方式吧
2023-03-20

详解C语言中strcpy函数与memcpy函数的区别与实现

这篇文章主要介绍了C语言中字符串拷贝函数(strcpy)与内存拷贝函数(memcpy)的不同及内存拷贝函数的模拟实现,感兴趣的小伙伴可以跟随小编一起学习一下
2022-12-29

C语言中的内联函数(inline)与宏定义(#define)详细解析

内联函数与宏本质上是两个不同的概念如果程序编写者对于既要求快速,又要求可读的情况下,则应该将函数冠以inline
2022-11-15

C语言形参和实参的区别详解

在函数定义和调用过程中,形参和实参是非常重要的概念,本文主要介绍了C语言形参和实参的区别,具有一定的参考价值,感兴趣的可以了解一下
2023-05-20

C语言中++a和a++的区别解析

c语言中++a和a++的区别:++a:先递增a的值,再返回递增后的值。a++:先返回a的当前值,再递增a的值。C语言中++a和a++的区别解析理解C语言中的++a和a++都是单目递增运算符。它们的目标是修改变量a的值,使a增加 1。区
C语言中++a和a++的区别解析
2024-04-03

C语言中*和&的区别及使用方法详解

在 c 语言中, 用于解引用指针,返回指向的值;&amp;amp;amp;amp;amp;amp; 用于取地址,返回指向该变量的指针。 通常用于访问或修改指针所指向的值;&amp;amp;amp;amp;amp;amp; 通
C语言中*和&的区别及使用方法详解
2024-04-03

详解Go语言中make和new的区别

Go语言中,有两个比较雷同的内置函数,分别是new和make方法,那他们有什么区别呢?本文将通过一些示例为大家详细介绍一下,感兴趣的可以了解一下
2023-02-26

深入理解Go语言中方法和函数的区别

在 go 语言中,方法与特定类型关联,方法名称带有接收器参数,用于操作特定类型的实例,而函数是独立代码块,不与任何类型关联,以普通参数的形式接收变量或值。Go语言中方法和函数的区别简介在Go语言中,方法和函数是代码中的两个重要概念,理解
深入理解Go语言中方法和函数的区别
2024-04-03

Go语言数组和切片的区别详解

本文主要介绍了Go语言数组和切片的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-14

C++ 友元函数详解:友元函数和成员函数的区别?

友元函数允许外部函数访问类中的私有或受保护成员,通过在类定义中用 friend 关键字声明。与成员函数不同,友元函数声明在类外部,可访问类的私有和保护成员,而成员函数在类内部声明,可访问类所有成员。友元函数用作普通函数调用,而成员函数用类对
C++ 友元函数详解:友元函数和成员函数的区别?
2024-04-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录