(本文是《C語言編程藝術(shù)》的一部分,轉(zhuǎn)載請注明出處,,勿用于商業(yè)用途,。)
大家一定對C語言 inline 關(guān)鍵字不陌生,甚至經(jīng)常用到 static inline 的函數(shù),??赡芨械侥吧氖?extern inline。C11 標(biāo)準(zhǔn)在6.7.4 p7 中對此進(jìn)行了描述,,不過比較生澀難讀,。簡單地講,static line 和 extern inline 的區(qū)別,,從名字上也能看得出來,,就是編譯單元之外可見性的問題。把一個普通函數(shù)給內(nèi)聯(lián)掉之后,,一個顯著的區(qū)別就是外部可見性沒了,,怎么能同時(shí)既能內(nèi)聯(lián)又能保留外部的可見性呢?為了解決這個問題就有了extern inline,。
static inline 是說這個函數(shù)就這么一個,,而且沒有外部可見性,在所有的編譯單元中大家必須共用這么一個定義,,這也是為什么 static inline 通常要放到頭文件中的原因,;而 extern inline 就不一樣了,它是說這個函數(shù)雖然只有一個定義,,但是既有內(nèi)聯(lián)的版本,,也有非內(nèi)聯(lián)的版本,,如果你能看得到它的定義,。即在同一個編譯單元中,那么你就可以用它的內(nèi)聯(lián)版本,;看不到的話,,你就可以用非內(nèi)聯(lián)版本,即和其它普通函數(shù)一模一樣。
而如果既不帶 static 也不帶 extern 的話,,含義又不同了:前面的 extern inline 只需要定義一次,,只是不同的地方看到的版本不同,有的地方看到的是內(nèi)聯(lián)的版本,,而有的地方看到的只是個 external reference,。而僅用 inline 的話,就變成了要定義兩次,,帶 inline 關(guān)鍵字的這個定義就是內(nèi)聯(lián)版本,,在這個編譯單元中都用這個版本,而在外部,,還是使用普通的非內(nèi)聯(lián)定義,。換句話說,帶 inline 的定義遮蓋住了外部的定義,。既然有兩個不同的版本,,那么也就需要保持這兩個版本的定義相同,否則就會有問題,。
以上是標(biāo)準(zhǔn)C的定義,,而 GNU89 的定義就不同了,基本上是把”extern inline”和”inline”的含義給交換了,,“static line” 的含義都是相同的,。看下面的標(biāo)準(zhǔn)C的例子:
[c] //a.c //C99 extern inline int max(int a, int b) { return a > b ? a : b; }
int a = 10; int b = 20; int foo(void) { return max(a, b); }
extern int bar(void);
int main(void) { return foo() + bar(); }
//b.c //C99 extern int max(int a, int b);
int bar(void) { int a = 10; int b = 20; return max(a, b); } [/c]
我們這么編譯: % gcc -std=c99 -O2 -Wall -W a.c b.c -o c
而我們?nèi)绻阉幾g成 GNU89 的話,,就會報(bào)錯: % gcc -std=gnu89 -O2 -Wall -W a.c b.c -o c /tmp/ccAJTzwY.o: In function bar':
b.c:(.text+0xb): undefined reference to max’ collect2: ld returned 1 exit status
很明顯,,因?yàn)?GNU89 中的 extern inline 需要兩個定義,明顯我們少了一個,。修改后的代碼如下:
[c] //a.c //GNU89 extern inline int max(int a, int b) { return a > b ? a : b; }
int a = 10; int b = 20; int foo(void) { return max(a, b); }
extern int bar(void);
int main(void) { return foo() + bar(); }
// b.c //GNU89 int max(int a, int b) { return a > b ? a : b; }
int bar(void) { int a = 10; int b = 20; return max(a, b); } [/c]
glibc 中就用到了 GNU89 的extern inline 特性,,在 ctype.h 中,tolower()/toupper() 的定義是: [c]
ifdef __USE_EXTERN_INLINES
extern_inline int
NTH (tolower (int c)) { return c >= -128 && c = -128 && c = -128 && c = -128 && c < 256 ? __ctype_toupper[c] : c; } [/c]
順便說一句,,gcc 提供了-fgnu89-inline 和 -fno-gnu89-inline 選項(xiàng)可在編譯時(shí)控制上述 inline 的行為,。
Stackoverflow 上有人做了一個很好的總結(jié),我翻譯了一下:
GNU89:
"inline": 函數(shù)可能會被內(nèi)聯(lián)掉,,非內(nèi)聯(lián)的版本總是會生成,,而且外部可見,因此內(nèi)聯(lián)的定義在本編譯單元中只能有一次,,其它地方看到的是非內(nèi)聯(lián)版本,。
"static inline":不會生成外部可見的非內(nèi)聯(lián)版本,可能會生成一個 static 的非內(nèi)聯(lián)版本,。它當(dāng)然可以被定義多次,,因?yàn)橥獠靠床坏剿?
"extern inline":不會生成一個非內(nèi)聯(lián)的版本,,但是可以調(diào)用一個非內(nèi)聯(lián)版本(因此你必須在其它編譯單元中定義它)。只能有一個定義的規(guī)則當(dāng)然也適用,,非內(nèi)聯(lián)版本和內(nèi)聯(lián)版本的代碼必須是一樣的,。
C99 (or GNU99):
"inline":和 GNU89 的 "extern inline" 一樣,沒有外部可見的函數(shù)生成,,但是外部可見的函數(shù)必須存在,,因?yàn)橛锌赡軙玫剿?
"extern inline":和 GNU89 的 "inline" 一樣, 會生成外部可見的代碼,,最多一個編譯單元可以使用它,。
"static inline":和 GNU89 的 "static inline" 一樣,這是唯一一個在 GNU89 和 C99之間可移植的,。
最后,,我們可以看大神 Linus Torvalds 如何描述 extern inline:
“static inline” means “we have to have this function, if you use it, but don’t inline it, then make a static version of it in this compilation unit”. “extern inline” means “I actually have an extern for this function, but if you want to inline it, here’s the inline-version”.
參考資料: http://www./rjk/tech/inline.html http://gcc./ml/gcc/2006-11/msg00006.html http:///questions/6312597/is-inline-without-static-or-extern-ever-useful-in-c99 http://www.cnblogs.com/cnmaizi/archive/2011/01/19/1939686.html
|