C99的inline Function
inline function是一個keyword,提醒compiler可以將function本體直接填入呼叫該function的位置。從這邊可以看到,優點為
- 減少呼叫function的overhead,如參數傳遞、回傳值處理等。
- 切換到function會jump,也就是會有branch的行為。因為CPU的pipeline和cache的特性,會影響效能。
- 經由代換程式碼,也許可以增加最佳化的可能性。
廢話少說,先來一段程式碼。 測試環境
- Ubuntu 12.04.4
GCC 4.6.3
inline_test.c
#include <stdio.h>
inline void hello()
{
printf("Hello World\n");
}
int main(void)
{
hello();
return 0;
}
可以正常編譯
$ cc -g -Wall -Werror -c -o inline_test.o inline_test.c
$ cc -g -Wall -Werror -o inline_test inline_test.o
有趣的是,換成C99就會編譯錯誤,檢查symbol的確不存在。
cc -g -Wall -Werror -std=c99 -c -o inline_test.o inline_test.c
cc -g -Wall -Werror -std=c99 -o inline_test inline_test.o
inline_test.o: In function `main':
inline_test.c:10: undefined reference to `hello'
collect2: ld returned 1 exit status
make: *** [inline_test] Error 1
$ nm inline_test.o
U hello
0000000000000000 T main
從這邊可以看到C99的inline
定義是和GNU C的extern inline
相反。所以最簡單的懶人法就是在C99裡面加上extern
收工。
- inline_test_c99.c
#include <stdio.h>
extern inline void hello()
{
printf("Hello World\n");
}
int main(void)
{
hello();
return 0;
}
加碼測試
不過對於組裝工而言,為何要在inline
前面加extern
或是static
實在有趣,所以多測了幾下。沒興趣的就直接看結論吧
GNU C測試
如果我們把程式碼改成
- test_inline.c
#include <stdio.h>
extern inline void hello()
{
int i = 100;
printf("Hello World: %d\n", i);
}
inline void hello()
{
printf("Hello World 2\n");
}
int main(void)
{
hello();
return 0;
}
在GNU C下編譯執行會印Hello World 2
,使用objdump -S
反組譯可以看到
- main 呼叫
4004f4
位址 4004f4
真正的程式碼是印出Hello World2
,也就是說extern inline void hello()
裡面的程式碼是寫心酸的。- 看起來這種情況GCC沒有把inline function展開。
另外一點有趣的是C99下面把extern inline
和inline
對調會編譯失敗。
00000000004004f4 <hello>:
int i = 100;
printf("Hello World: %d\n", i);
}
inline void hello()
{
4004f4: 55 push %rbp
4004f5: 48 89 e5 mov %rsp,%rbp
printf("Hello World 2\n");
4004f8: bf 0c 06 40 00 mov $0x40060c,%edi
4004fd: e8 ee fe ff ff callq 4003f0 <puts@plt>
}
400502: 5d pop %rbp
400503: c3 retq
0000000000400504 <main>:
int main(void)
{
400504: 55 push %rbp
400505: 48 89 e5 mov %rsp,%rbp
hello();
400508: b8 00 00 00 00 mov $0x0,%eax
40050d: e8 e2 ff ff ff callq 4004f4 <hello>
return 0;
400512: b8 00 00 00 00 mov $0x0,%eax
}
結論
- GNU C和C99 inline的
extern
定義相反。 - C99下面只有
inline
只是一個宣告,不會產生symbol。要使用extern
編譯器才會產生symbol。猜測可能單純inline
是在header file宣告用,而extern inline
則是在source code實作時使用。