关于内联函数

前言

在程序设计中为什么要使用函数,首先来看下函数的定义:许多程序设计语言中,可以将一段经常需要使用的代码封装起来,在需要使用时可以直接调用,所以,函数也可以说是许多代码的集合。也就是说把实现某一功能的代码进行封装起来形成函数。
函数的类型有很多,这里主要比较普通函数,宏定义函数以及内联函数。

1. 普通函数

普通函数是相对常见的函数,将实现某一功能的代码封装成普通函数,同时也会付出相应的代价。下面代价的分析引自这篇文章C语言宏定义和宏定义函数

1.函数调用会带来额外的开销,它需要开辟一片栈空间,记录返回地址,将形参压栈,从函数返回还要释放堆栈(一个程序被分配的堆栈空间是有限的,否则堆栈空间会耗竭)。这种开销不仅会降低代码效率,而且代码量也会大大增加;
2.函数的参数必须被声明为一种特定的类型,所以它只能在类型合适的表达式上使用,我们如果要比较两个浮点型的大小,就不得不再写一个专门针对浮点型的比较函数。还有一些任务根本无法用函数实现,比如参数类型没法作为参数传递给函数,但是用宏定义却很好实现。

2. 宏定义函数

首先看宏定义函数长什么样:

#include<iostream>
using namespace std;

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

int main()
{
    int a=MAX(2,3);
    cout<<a<<endl;//3
    return 0;
}

使用宏定义函数则在代码规模和速度方面都比普通函数更胜一筹,在宏函数中并没有定义变量的类型,所以这个宏定义可以用于整形、长整形、单浮点型、双浮点型以及其他任何可以用“>”操作符比较值大小的类型,也就是说,宏是与类型无关的。和使用函数相比,使用宏的不利之处在于每次使用宏时,一份宏定义代码的拷贝都会插入到程序中,除非宏非常短,否则使用宏会大幅度增加程序的长度。(内联函数也有这个缺点)

需要注意的是:宏是通过文本替换方式实现的,定义体里的参数被调用的实际参数替换,得到的结果再作为文本去替换原来的调用段。这种做法与函数不同,这种傻傻的替换方式,常给人带来一些麻烦。假如square是个函数,表达式:1/ square(x)的结果复合我们的意图,将1除以square(x)的结果。
如果采用宏定义函数的方法:

#define square(x) (x)*(x)

1/ square(x)的结果是:1/(x)*(x),这显然不符合我们的意图。
所以需要这样定义:

#define square(x) ((x)*(x))

当表达式的运算相对复杂时,从这里也可以看出来使用宏函数的缺点。

#define REGS_FOREACH(_) _(X) _(Y)
#define RUN_LOGIC 		X1 = !X && Y; \
						Y1 = !X && !Y;
#define DEFINE(X) 		static int X, X##1;
#define UPDATE(X) 		X = X##1;
#define PRINT(X) 		printf(#X"=%d;",X); 
int main() {
	REGS_FOREACH(DEFINE); 
	while (1) { // clock 
		RUN_LOGIC; 
		REGS_FOREACH(PRINT); 
		REGS_FOREACH (UPDATE); 
		putchar('\n'); sleep(1);
	}
	return 0;
}

替换后的结果:

int main() {
	static int X, X1; static int Y, Y1;; 
	while (1) {
		X1 = !X && Y; Y1 = !X && !Y;; 
		printf("X" "%d;",X); printf("Y" " = %d;", Y);;
		X = X1; Y = Y1;; 
		putchar('\n'); sleep(1);
	}
	return 0;
}

可以看到宏函数就是简单的宏替换策略,但确实又有函数传参的效果,用宏去解决问题还是一种函数的思想。

可以通过gcc的预编译宏展开gcc -E main.c来查看宏展开后的代码。

3. 内联函数

内联函数由inline关键字定义,内联函数的作用其实和宏函数的作用差不多,都是在函数调用的位置,将函数的内容展开,避免普通函数调用效率低的缺点,同时也具有普通函数实现的优点,但如果执行函数体内代码的时间,相比于函数调用的时间较大,那么效率的收获也会很少。
关于内联函数的说明参见这篇文章
关于内联函数为什么在头文件定义,这篇文章也给出了很好的解释。

inline函数的定义一般放在头文件中,其他源文件要用的时候,直接把定义了inline函数的头文件include进来即可。如果在源文件中定义,在头文件中声明,则会变成为非内联函数的调用,因为每个源文件都是独立编译的,编译完之后只能按照普通函数一样调用。

也就是说如果在源文件中定义,在头文件中声明,那么其他源文件要调用这个函数,只有在链接的时候实现,那么相当于普通函数的调用。


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是浩浩子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值