内联函数说明符

内联函数是指编译器将代码从函数定义直接复制到调用函数的代码中,而不是在内存中创建单独的指令集。 可以直接将函数主体的修改副本替换为函数调用,而不是将控制转移到函数代码段和从函数代码段转移。 这样就避免了函数调用的性能开销。 使用 inline 说明符只是对编译器的建议,即可以执行内联扩展; 编译器可以随意忽略该建议。

C 仅开始 除了 ,任何函数都可以通过 函数说明符进行声明或定义。 main inline 静态局部变量不允许在嵌入式函数的主体中定义。仅限 C 结束

仅限 C + + 开始 在类声明中实现的C++函数会自动定义为内联函数。 可以使用 inline 函数说明符来声明或定义在类声明外部声明的常规 C++ 函数和成员函数 ( main除外)。 在嵌入式函数体内定义的静态局部变量和字符串字面量在翻译单元中被视为同一对象;详情请参阅嵌入式函数的链接仅限 C++ 结束

以下代码片段显示了内联函数定义:
inline int add(int i, int j) { return i + j; }
使用 inline 说明符不会更改函数的含义。 但是,函数的内联扩展可能不会保留实际自变量的求值顺序。

对内联函数进行编码的最有效方法是将内联函数定义放在头文件中,然后将头包含在包含对要内联的函数的调用的任何文件中。

注: inline 说明符由以下关键字表示:
  • C 仅开始 inline 关键词在编译时与 xlcc99-qlanglvl=stdc99-qlanglvl=extc99 选项或 -qkeyword=inline__inline__ (或 __inline) 关键字在所有语言级别都可识别; 但是,请参阅下面的 内联函数的链接 以获取此关键字的语义。
  • 仅限 C + + 开始 inline、 和 关键词在所有语言级别均被识别。 __inline __inline__

内联函数的链接

C 仅开始
在 C 中,缺省情况下,内联函数被视为具有 静态 链接; 即,它们仅在单个转换单元中可视。 因此,在以下示例中,即使以完全相同的方式定义函数 foo ,也会将文件 a.c 中的 foo 和文件 b.c 中的 foo 视为单独的函数: 生成两个函数主体,并在内存中分配两个不同的地址:
// a.c

#include <stdio.h>

inline int foo(){
   return 3;
}

void g() {
   printf("foo called from g: return value = %d, address = %p\n", foo(), &foo);
}


// b.c

#include <stdio.h>

inline int foo(){
   return 3;
}

void g();

int main() {
   printf("foo called from main: return value = %d, address = %p\n", foo(), &foo);
   g();
}
编译的程序的输出为:
foo called from main: return value = 3, address = 0x10000580 
foo called from g: return value = 3, address = 0x10000500
由于内联函数被视为具有内部链接,因此内联函数定义可以与另一个转换单元中具有相同名称的函数的常规外部定义共存。 但是,当您从包含内联定义的文件中调用函数时,编译器可以选择 在同一文件中定义的内联版本 在另一个文件中为调用定义的外部版本; 您的程序不应依赖于所调用的内联版本。 在以下示例中,从函数 g 调用 foo 可能返回 6 或 3:
// a.c

#include <stdio.h>

inline int foo(){
   return 6;
}

void g() {
   printf("foo called from g: return value = %d\n", foo());
}


// b.c

#include <stdio.h>

int foo(){
   return 3;
}

void g();

int main() {
   printf("foo called from main: return value = %d\n", foo());
   g();
}
同样,如果将函数定义为 extern inline,或者将 inline 函数重新声明为 extern,那么该函数将成为常规的外部函数,并且不会直接插入。

IBM 扩展开始 如果您指定了 关键字,并带有下划线,则编译器将使用GNU C语义的内联函数。 __inline__ 与 C99 语义相反,定义为 __inline__ 的函数仅提供外部定义; 定义为 static __inline__ 的函数提供具有内部链接的内联定义 (如 C99中所示); 定义为 extern __inline__的函数在启用优化的情况下进行编译时,允许同一函数的内联定义与外部定义共存。 有关内联函数 GNU C 实现的更多信息,请参阅 GCC 信息,网址为 http://gcc.gnu.org/onlinedocs/IBM 扩展结束

仅限 C 结束

仅限 C + + 开始 在每个使用或调用内联函数的翻译单元中,必须以完全相同的方式定义该函数。 此外,如果函数定义为 inline,但从未在同一转换单元中使用或调用,那么编译器将废弃该函数 (除非使用 -qkeepinlines 选项进行编译)。

但是,在 C++ 中,缺省情况下,内联函数被视为具有 外部 链接,这意味着程序的行为就像只有一个函数副本一样。 该函数将在所有转换单元中具有相同的地址,并且每个转换单元将共享任何静态本地和字符串字面值。 因此,编译先前示例将提供以下输出:
foo called from main: return value = 3, address = 0x10000580 
foo called from g: return value = 3, address = 0x10000580
重新定义具有相同名称但具有不同函数体的内联函数是非法的; 但是,编译器不会将此标记为错误,而只是为在编译命令行上输入的第一个文件中定义的版本生成函数体,并废弃其他函数体。 因此,以下示例 (在两个不同文件中以不同方式定义了内联函数 foo ) 可能不会生成预期结果:
// a.C

#include <stdio.h>

inline int foo(){
   return 6;
}

void g() {
   printf("foo called from g: return value = %d, address = %p\n", foo(), &foo);
}


// b.C

#include <stdio.h>

inline int foo(){
   return 3;
}

void g();

int main() {
   printf("foo called from main: return value = %d, address = %p\n", foo(), &foo);
   g();
}
使用命令 xlc++ a.C b.C 编译时,输出为:
foo called from main: return value = 6, address = 0x10001640
foo called from g: return value = 6, address = 0x10001640
main 调用 foo 不会使用 b.C中提供的内联定义,而是将调用 foo 作为 a.C中定义的常规外部函数。 您负责确保具有相同名称的内联函数定义在转换单元之间完全匹配,以避免意外结果。
由于内联函数被视为具有外部链接,因此在内联函数的主体中定义的任何静态局部变量或字符串字面值都被视为跨转换单元的同一对象。 以下示例对此进行了演示:
// a.C

#include <stdio.h>

inline int foo(){
   static int x = 23;
   printf("address of x = %p\n", &x);
   x++;
   return x;
}

void g() {
   printf("foo called from g: return value = %d\n", foo());
}


// b.C

#include <stdio.h>

inline int foo()
{
   static int x=23;
   printf("address of x = %p\n", &x);
   x++;
   return x; 
}

void g();

   int main() {
   printf("foo called from main: return value = %d\n", foo());
   g();
}
此程序的输出显示 foo 的两个定义中的 x 确实是同一对象:
address of x = 0x10011d5c
foo called from main: return value = 24
address of x = 0x10011d5c
foo called from g: return value = 25

如果要确保定义为内联的函数的每个实例都被视为单独的函数,那么可以在每个转换单元的函数定义中使用 static 说明符,或者使用 -qstaticinline 选项进行编译。 但是请注意,静态内联函数在模板实例化期间从名称查找中移除,因此无法找到。 仅限 C++ 结束