Inline In C99
Table of Contents
1. Inline In C99
1.1. The Problem
#include <stdio.h> inline int foo() { return 0x64; } int main() { printf("Output is: %d\n", foo()); return 0; }
Output is: 100
上面的代码使用 c99 且用 `-O2` 时可以编译过, 且 foo 被 inline. 但使用 `-O0` 则编译失败, 错误为 undefined reference to `foo`
1.2. 原因
针对 inline 关键字, c99 与 c89 的意义不同.
在 c89 中, inline 即 static inline, 通常的用法是把 static inline 定义的函数放在头文件中, 然后所有需要使用这个函数的 c 去 include 这个头文件.
1.2.1. 重复的 foo
static inline 是 internal linkage, 所以如果编译器没有成功 inline (例如使用了 -O0), 则每个编译单元中都会有一个 foo 的实现且 link 时不会出现 `multiple definition` 的错误.
inline 的好处是省去函数调用的开销,坏处是需要更多的代码,因为每个函数调用都会被展开。 但是在 c89 的情况下,若编译器没有成功 inline,则既没有节省函数调用开销,又需要更多的代码。
/* test.h */ static inline void foo() {} /* a.c */ int main(int argc, char *argv[]) { foo(); }
/* test.h */ static inline void foo() {} /* b.c */ void b() { foo(); }
gcc /tmp/a.o /tmp/b.o -o /tmp/a.out objdump -d /tmp/a.out |grep \<foo\>: -A 5
0000000000001129 <foo>: 1129: 55 push %rbp 112a: 48 89 e5 mov %rsp,%rbp 112d: 90 nop 112e: 5d pop %rbp 112f: c3 retq – 0000000000001154 <foo>: 1154: 55 push %rbp 1155: 48 89 e5 mov %rsp,%rbp 1158: 90 nop 1159: 5d pop %rbp 115a: c3 retq
1.2.2. 不同的 foo
由存在多个重复的 foo, 所以这些 foo 的地址都是不同的,看起来会违反直觉
1.3. Inline In C99
C99 的 inline 是为了解决上面的两个问题
需要在共同的头文件中用 inline 定义 foo 函数,保证成功 inline 时编译器在各个编译单元都知道如何展开
/* test.h */ inline void foo() {}
在某一个编译单元里用 extern inline 声明一个 foo 函数,使得 inline 失败时都会 link 到这一个 foo 函数
/* a.c */ extern inline void foo(); /* 或者用 void foo(); */
例如:
1.3.1. a.o 包含 foo 的实现
inline void foo() {} extern inline void foo(); int main(int argc, char *argv[]) { foo(); return 0; }
readelf -a /tmp/a.o|grep foo
000000000024 000900000004 R_X86_64_PLT32 0000000000000000 foo - 4 9: 0000000000000000 11 FUNC GLOBAL DEFAULT 1 foo
1.3.2. b.o 不包含 foo 的实现
inline void foo() {} void hello() { foo(); }
readelf -a /tmp/b.o|grep foo
00000000000e 000b00000004 R_X86_64_PLT32 0000000000000000 foo - 4 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo