一般来说,调用一个函数流程为:当前调用命令的地址被保存下来,程序流跳转到所调用的函数并执行该函数,最后跳转回之前所保存的命令地址。 对于需要经常调用的小函数来说,这
对于需要经常调用的小函数来说,这大大降低了程序运行效率。所以,C99 新增了内联函数(inline function)。
关键字 inline 告诉编译器,任何地方只要调用内联函数,就直接把该函数的机器码插入到调用它的地方。这样程序执行更有效率,就好像将内联函数中的语句直接插入到了源代码文件中需要调用该函数的地方一样。
要将一个函数定义为内联函数,需要在函数定义时加上 inline 函数修饰符。例 1 中,swapf()被定义为内联函数,用来交换两个浮点变量的值,函数 selection_sortf()会调用内联函数 swapf()。
【例1】函数 swapf()
// 函数swapf()交换两个浮点变量的值 // 参数:两个指向float的指针 // 返回值:无 inline void swapf( float *p1, float *p2 ) // 一个内联函数 { float tmp = *p1; *p1 = *p2; *p2 = tmp; } // 函数selection_sortf() 使用 selection-sort算法 // 对float数组进行排序 // 参数:一个float数组,以及其长度 // 返回值:无 void selection_sortf( float a[], int n ) // 对长度为n的数组进行排序 { register int i, j, mini; // 3个索引变量 for ( i = 0; i < n - 1; ++i ) { mini = i; // 从索引i开始,寻找最小值 for ( j = i+1; j < n; ++j ) if ( a[j] < a[mini] ) mini = j; swapf( a+i, a+mini); // 交换最小值元素和索引i元素的值 } }
一般来说,不建议把将包含循环的函数定义成内联函数,例如函数 selection_sortf()。例 1 在 for 循环中使用内联函数来加速执行效率。
inline 修饰符并非强制性的:编译器有可能会置之不理。例如,递归函数通常不会被编译成内联函数。编译器有权自行决定是否要将有 inline 修饰符的函数编译成内联函数。
和其他函数不同的是,在每个用到内联函数的翻译单元中,都必须重复定义这个内联函数。编译器必须时刻准备好该函数定义,以便在调用它时及时插入内联代码。因此,经常在头文件中定义内联函数。
如果某个翻译单元内的某个函数的所有声明都具有 inline 修饰符,而没有 extern 修饰符,那么该函数具有内联定义(inline definition)。
内联定义只针对翻译单元,它不构成外部定义,因此别的翻译单元可以包含该函数的外部定义。如果有外部定义附加到内联定义中,那么编译器可以自由选择要使用哪一种定义。
如果使用存储类修饰符 extern 来声明一个采用 inline 定义的函数,那么该函数的定义就会是外部的(external)。例如,下面的声明与 swapf()的定义如果放在例 1 的同一个翻译单元中,那么 swapf()就具有 extern 定义:
extern void swapf( float *p1, float *p2 );
一旦函数 swapf()具有外部的定义,其他翻译单元只需要采用普通的函数声明,就可以调用它。然而,从别的翻译单元调用函数,将不会被编译成内联函数。
内联函数其实就是普通函数,只不过它们在调用时采用机器码形式。和普通函数一样,内联函数具有自己的地址。如果内联函数使用到宏,预处理器就会展开宏,展开时所用的宏值,取该内联函数在源代码中定义所在位置的宏值。然而,在没被声明为 static 的内联函数中,不应该以静态存储周期的方式来定义可修改的对象。