2014-02-14 68 views
0

我在閱讀其他代碼時遇到了一個有趣的情況。訪問靜態變量時內聯函數和靜態內聯函數之間的區別

在頭文件中,定義的靜態變量和一個內聯函數簡化爲以下:

static int ply; 

inline int WTM(){return ply;} 

和被調用的函數在某些其它cpp文件包括此頭。

cout << ply << " " << WTM(); 

奇怪的是,在這裏這個函數被調用,內聯函數內部的變量ply具有從功能之外的相同變量之前它不同的值。

輸出爲0 1;

我檢查了所有的文件,plyWTM()都有這個單一的定義。

之後,我已經改變了代碼如下:

static int ply; 

static inline int WTM(){return ply;} 

兩個值成爲相同。

我的編譯器是g++ (GCC) 4.4.7使用默認設置。

我搜索了這個現象,並獲得這兩鏈接: Difference between an inline function and static inline functionhttp://gcc.gnu.org/onlinedocs/gcc/Inline.html 但還是不明白爲什麼會發生(尤其是他們爲什麼會在第一種情況下有不同的值)。我不知道是否有人可以告訴我編譯器如何擴展這兩段代碼(我嘗試使用-E,但它似乎不適用於內聯函數)。

+0

我不明白「1」來自哪裏。無論範圍如何,它應該是0. – starmole

+0

@starmole I * guess *在一個翻譯單元中有一個作業。 –

+0

是的,實際上還有很多其他作業。 –

回答

5

這是因爲靜態變量將在包含頭文件的所有翻譯單元中單獨定義,但(非靜態)函數將只定義一次。所以你有多個副本的變量,但只有一個函數副本。該函數將使用哪個副本?我不知道,我認爲這是未定義的行爲或實現定義(必須閱讀規範)。

當您聲明函數爲static時,它會在每個翻譯單元中與變量相同,因此僅爲該翻譯單元訪問該變量。

+0

是不是也可以質疑該函數的哪個副本已被鏈接?每個目標文件都會定義該函數,所以我認爲返回的'ply'變量將來自最終鏈接函數。 – jthill

+0

非常感謝。我試圖打印他們的地址,他們有不同的地址。 –

-1

通過使用參考What's the difference between 「static」 and 「static inline」 function?

inline指示編譯器試圖嵌入函數內容到調用代碼,而不是在執行實際的呼叫的。

對於頻繁調用的小功能可能會產生巨大的性能差異。

但是,這只是一個「提示」,編譯器可能會忽略它,即使關鍵字沒有被使用,大多數編譯器也會嘗試使用「inline」作爲優化的一部分。

例如:

static int Inc(int i) {return i+1}; 
.... // some code 
int i; 
.... // some more code 
for (i=0; i<999999; i = Inc(i)) {/*do something here*/}; 

這種緊密的循環將執行每次迭代函數調用,函數的內容實際上比編譯器需要把執行調用的代碼顯著少。內聯將主要指示編譯器的代碼轉換上述成等價物:

int i; 
.... 
for (i=0; i<999999; i = i+1) { /* do something here */}; 

跳過實際的函數調用和返回

顯然,這是展示點,而不是一個真正的代碼示例。

static指的是範圍。在C中,這意味着函數/變量只能在相同的翻譯單元中使用。

+0

對於函數使用'inline'只是一個*建議*,如果可以的話,編譯器*可以內聯代碼。這不是要求。 –

+1

是的,但內聯還可以在違反odr的多個翻譯單元中包含相同的功能。 –

0

你第一次使用沒有靜態的內聯函數是未定義的行爲。

標準3.2.6,

可以有超過...,...一個定義,內聯函數 具有外部鏈接(7.1.2),...,在一個程序條件是每個 定義出現在不同的翻譯單元中,並且提供的 定義滿足以下要求:

- D的每個定義應由相同的令牌序列組成;和

- 在d中的每個定義,相應的名稱,擡頭 根據3.4,應指d的定義 內定義一個實體,或應當指的是相同的實體,

- .. 。

如果d的定義滿足所有這些要求,那麼 程序應表現爲,如果有d的單一的定義。如果d的 定義不滿足這些要求,則行爲 是不確定的。

在第一次使用外部聯函數(不帶靜電),簾布層的名稱,名稱查找後,指向不同的實體不同的翻譯單元。

0

也有這樣一個更多的解釋:

通過聲明一個函數inline, 可以直接編譯器將這個函數的代碼集成到調用函數的代碼。

這使得執行速度更快,消除了函數調用開銷;另外,如果任何實際參數值是常量,它們的已知值可能會在編譯時允許簡化,因此不需要包含所有內聯函數的代碼。

對代碼大小的影響不太可預測;對象代碼可能更大或更小,具體取決於具體情況,功能inline

Inline功能是一種優化,它真的只在優化編譯「作品」。如果你不使用-O,沒有功能真的是inline

和對於static inline function

當一個函數同時inlinestatic

如果對該函數的所有調用都集成到調用,而該函數的地址從來沒有使用過,那麼該函數自身的彙編代碼從未被引用。

在這種情況下,compiler實際上並不輸出彙編代碼的功能,除非你指定選項-fkeep-inline-functions.

一些調用不能被集成爲前面函數的定義不能種種原因(特別是電話集成的,在定義內既不能遞歸調用)

如果有一個非集成的呼叫,則該函數被編譯到彙編代碼如常。如果程序引用其地址,則該函數也必須照常編譯,因爲該函數不能是inlined