对于了解C编程的人来说,inline函数不是什么难点,但是当阅读Linux内核时候,发现其中有除了inline还有static inline以及extern inline,如何理解这几种内联函数之间的区别是深入学习C和linux内核的关键。
inline关键字只是一种内联展开的建议,不是强制行措施,如果函数中使用了可变参数、内存分配函数,可变长度数据类型,非局部goto语句,递归语句等时,不会进行递归展开。
首先来看static inline。简单的理解就是static+inline。这种函数只在函数定义的文件中可见,外部不可见,因此允许外部有重名的函数。在调用这种函数时,GCC会在其调用处将其汇编代码展开编译而不为这个函数生成独立的汇编代码。除非函数地址被使用时,如通过函数指针对函数进行了间接调用,这时就不得不为其生成独立的汇编代码,否则函数没有自己的地址。
其次再来看inline函数:在其定义的文件里,其表现同static inline,在能展开时被内联展开编译。但是为了能在文件外被调用,GCC会为他生成一份独立的汇编代码。即GCC的的inline函数是全局的,在文件内可作为内联函数被内联展开,文件外通过直接调用而不是展开。
最后是extern inline函数:这是inline中最容易理解错误的一种,它不是ertern和inline的简单叠加,extern inline只会被内联展开而绝不会让编译器生成独立汇编代码,即使是通过指针调用,当出现这种情况的时候,对此函数的调用会被连接为一个外部引用。extern inline函数允许和外部函数重名,综上extern inline函数类似于一个宏定义。
下面举例说明:
在foo.c中有对foo函数的定义:
foo.c:
extern inline int foo(int x)
{
return (-x);
}
void func1()
{
...;
a = foo(a); ①
p_foo = foo; ②
b = p_foo(b); ③
}
在foo1.c中也定义了一个全局函数foo:
foo1.c:
int foo(int x)
{
return (x);
}
在上面那个例子里面,后面一个对foo函数地址的引用就会在链接时被指到这个foo2.c中定义的foo函数去。也就是说:①调用foo函数的结果是a=-a,因为其内联了foo.c内的foo函数;而③调用的结果则是b=b,因为其实际上调用的是foo2.c里面的foo函数!
由此可以总结出extern inline的两种主要用处:
①表现得像宏一样,可以在文件内用extern inline版本的定义取代外部定义的库函数(前提是文件内对其的调用不能出现无法内联的情况);
②它可以让一个库函数在能够被内联的时候尽量被内联使用。
注:本文特指GCC中的inline,与C99版本有差异。