C/C++中的内联函数和宏定义区别

内联函数和宏定义

前段时间被面试问了内联函数作用,一直只模糊知道就是提高效率,回来仔细看了书,很多细节需要注意。在说内联函数之前,必须理解预处理宏。内联函数的功能和预处理宏的功能相似但也有不同。

 

一.宏定义的优点与缺陷:

之所以用宏定义:因为函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方。这种转移操作要求在转去执行前要保存现场并记忆执行的地址,转回后要恢复现场,并按原来保存地址继续执行。因此,函数调用要有一定的时间和空间方面的开销,于是将影响其效率。

宏只是在预处理的地方把代码展开,不需要额外的空间和时间方面的开销,所以调用一个宏比调用一个函数更有效率
  但是宏也有缺陷:
1、宏不能访问对象的私有成员。
2、宏的定义很容易产生二义性。(容易出错)
举个例子,宏定义一个乘法函数:   #define multi(x) (x*x)

先用multi(10)这样调用看上去没有什么错误,结果返回100,是正确的,但是如果用multi(10+10)去调用的话,我们期望的结果是400,而宏的调用结果是(10+10*10+10),结果是120,这显然不是我们要得到的结果。避免这些错误的方法,一般是给宏的参数都加上括号。如是:  #define multi(x) ((x)*(x))

这样可以确保不会出现上面那样的错,但是,即使使用括号这种定义,这个宏依然有可能出错,例如使用multi(a++)调用它,我们期望得到(a+1)*(a+1)的结果,而实际宏的展开结果: (a++)*(a++),如果a的值是4,我们得到的结果是5*6=30。而我们期望的结果是5*5=25,这又出现了问题,++执行了两次。

这些宏定义难以避免的问题,我们可以用内联函数来,用内联函数来取代宏定义。


二.内联函数的优点及声明使用

内联函数和宏的区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。(但节约时间的同时增加了空间的消耗)

 

我们用inline来定义内联函数,在C++中,在类的声明部分定义了函数体的函数,被默认为是内联函数。而不管你是否有inline关键字。(不过编译器会决定要不要将它内联)。我们也可以将定义在类的外部的函数定义为内联函数。内联函数必须是和函数体申明在一起才有效。像这样的申明inlinefunction(int i)是无效的,编译器只是把函数作为普通的函数申明,我们必须定义函数体。

例如: inlinefunction(inti ) {return i*i };

这样才算定义了一个内联函数。我们可以把它作为一般的函数一样调用。但是执行速度确比一般函数的执行速度要快。
   内联函数主要分成两种,一种是类成员内部的内联函数,一种是类外面的全局内联函数:

1.类内部的内联函数:在C++的类成员中,如果成员函数的函数体本身结构不复杂,代码量也较少的时候,直接在定义这个函数的时候就完成该函数的实现,这样的一个过程在C++类中会被默认当做内联函数。

2.类外部的内联函数,当将一个全局函数定义成内联函数的时候,需要加一个inline 的关键字说明,相当于告诉编译器建议将该函数当内联函数进行处理。

注意:inline关键字仅仅是建议编译器做内联展开处理,而不是强制。被"inline"修饰的函数不一定被内联(但是无"inline"修饰的函数一定不是,类内部函数一定是)。而且内联使用不恰当是会有副作用的,会带来代码膨胀,还有可能引入难以发现的程序问题。如果内联函数的函数体过大,一般的编译器会放弃内联方式,而采用普通的方式调用函数。这样,内联函数就和普通函数执行效率一样了。


三.内联函数需要注意和不能内联的情况

使用内联函数的主要作用是减少函数调用引起的过多的内存消耗,在使用内联函数中应当要注意

1.      在定义内联函数应当尽量定义成那些调用非常频繁而且函数功能简单的函数,而不能将函数实现很复杂的功能定义成内联

2.      由于内联函数在调用的时候直接暴露出了函数体,故会造成泄漏函数功能的现象

3.      在内联函数中尽量减少变量的申请,尽量简化内联函数的函数体,这也对程序结构有好处

4.      因为内联函数要在调用处展开,编译器必须能在每一个调用处能看到该函数的定义,因此最好将函数实现放在头文件中

5.      在实现文件中该函数之前要加上inline关键字的方式是有问题的:如果调用的obj文件在函数定义之前生成,那么该处就无法嵌入内联函数了。

 

以下不会被内联的几种情况:    

1.包含了递归、循环等结构的函数一般不会被内联。

2.虚拟函数一般不会内联,但是如果编译器能在编译时确定具体的调用函数,那么仍然会就地展开该函数。

3.如果通过函数指针调用内联函数,那么该函数将不会内联而是通过call进行调用。

4.构造和析构函数一般会生成大量代码,因此一般也不适合内联。

5.如果内联函数调用了其他函数也不会被内联。

 


四. 内联函数和宏定义的比较

总结内联函数和宏定义的不同:

1.       内联函数在运行时可调试,而宏定义不可以;

2.       编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会; 

3.       内联函数可以访问类的成员变量,宏定义则不能; 

C++ 语言支持函数内联,其目的是为了提高函数的执行效率(速度)。 而在C程序中,可以用宏代码提高执行效率。宏代码本身不是函数,但使用起来象函数。预处理器用复制宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的 CALL调用、返回参数、执行return等过程,从而提高了速度。使用宏代码最大的缺点是容易出错,预处理器在复制宏代码时常常产生意想不到的边际效应。当使用内联函数的时候,起目的是为了消除函数调用上的开销,频繁的调用函数会增加内存上的开销。通过一个内联函数可以得到所有宏的替换效能和所有可预见的状态以及常规函数的类型检查。

这篇文章http://www.cnblogs.com/xkfz007/articles/2370640.html  详细说了static inline 和 externinline的用法以及gcc 和c99里的inline的区别。摘了点:

static inline可以认为是一个static的函数,加上了inline的属性。这个函数大部分表现和普通的static函数一样,只不过在调用这种函数的时候,gcc会在其调用处将其汇编码展开编译而不为这个函数生成独立的汇编码。除了以下几种情况外:
(1)函数的地址被使用的时候。如通过函数指针对函数进行了间接调用。这种情况下就不得不为static inline函数生成独立的汇编码,否则它没有自己的地址。
(2)其他一些无法展开的情况,比如函数本身有递归调用自身的行为等。
static inline函数和static函数一样,其定义的范围是local的,即可以在程序内有多个同名的定义(只要不位于同一个文件内即可)。
(3)gcc的static inline函数仅能作用于文件范围内;c的inline函数是全局性的:在文件内可以作为一个内联函数被内联展开,而在文件外可以调用它。

而 externinline的函数只会被内联进去,而绝对不会生成独立的汇编码!另外,extern inline的函数允许和外部函数重名,即在存在一个外部定义的全局库函数的情况下,再定义一个同名的extern inline函数也是合法的。

extern inline的用法。第一:它可以表现得像宏一样,可以在文件内用extern inline版本的定义取代外部定义的库函数(前提是文件内对其的调用不能出现无法内联的情况);第二:它可以让一个库函数在能够被内联的时候尽量被内联使用。
注意:gcc绝对不会为extern inline的函数生成独立汇编码。




猜你喜欢

转载自blog.csdn.net/zcyzsy/article/details/52933695