推 EdisonX: 以 C 而言,global func. 加上 static 修飾 , 代表只能在 06/15 03:13
→ EdisonX: 該檔案被呼叫, 如 static void func() 放在 a.c , 就不能 06/15 03:13
→ EdisonX: 在 b.c 呼叫 func() ; 若不加 static 時,即使 a.h 沒加 06/15 03:14
→ EdisonX: 上void func();之宣告, b.c 仍可事先用extern void fun() 06/15 03:15
→ EdisonX: 宣告後,調用在 a.c 裡的 func() 06/15 03:15
→ EdisonX: 合在一起看,static inline void func(); 代表這個函式只 06/15 03:16
→ EdisonX: 能在某個特定檔案裡面被呼叫 , 而且在此檔案裡會建議編譯 06/15 03:16
→ EdisonX: 器直接展開 , 並沒衝突。(真正衝突的是extern和static) 06/15 03:17
→ EdisonX: 若以 c++ 來講,放在 class 裡面的話又是另一個故事了... 06/15 03:17
所以說當inline沒被compiler接受的時候....
inline -> 變成普通函式 -> 外部可用extern呼叫
static inline -> 變成普通static函式 -> 外部不可用extern
extern inline -> 變成普通函式 -> 外部可用extern呼叫
當inline被compiler接受時:
inline -> 變成inline -> 外部不可用extern
static inline -> 變成inline -> 外部不可用extern
extern inline -> 變成inline+普通函式 -> 外部可用extern呼叫
請問我的理解有錯嗎?
其實我覺得以現在的compiler的強度根本可以自己決定要不要inline...
這功能變得有點雞肋....
※ 編輯: wtchen (90.41.66.248), 06/15/2016 04:20:50
推 CoNsTaR: 沒加 static 別人可以自己用 extern 宣告 06/15 17:26
→ CoNsTaR: inline 的意義是告訴使用者 “定義和宣告合併” 06/15 17:29
→ wtchen: 不管inline有沒有成功都是嗎? 06/15 17:29
→ CoNsTaR: 隱含意義是 “只會有這個實作版本” 因為實作和宣告寫在 06/15 17:30
→ CoNsTaR: 一起了 06/15 17:30
→ CoNsTaR: 至於把程式片段取代函式呼叫 那只是編譯器自己的優化 06/15 17:32
→ CoNsTaR: inline 沒有成功與否啊 它只是用來提供資訊的關鍵字而已 06/15 17:33
推 CoNsTaR: 對了 GNU 的 inline 效果和 srandard 不一樣 06/15 17:40
→ CoNsTaR: 但是兩者對 inline 的定義卻是相同的 06/15 17:42
→ CoNsTaR: 兩個都拿來看看應該可以找出 inline 的本質 06/15 17:42
關於inline的定義在Standard 是6.7.4,不過他的寫法我不是很理解。
所以大部份我是參考 http://www.greenend.org.uk/rjk/tech/inline.html
這邊有一段是這樣:
A function where all the declarations (including the definition)
mention inline and never extern.
There must be a definition in the same translation unit.
The standard refers to this as an inline definition.
No stand-alone object code is emitted,
so this definition can't be called from another translation unit.
我的理解是,compiler看到inline可以自己決定他要真的inline還是維持普通函式。
inline的話,因為變成用Macro的方式代換函式,自然也沒有必要保留此函式的指標了。
至於gcc,參考https://gcc.gnu.org/onlinedocs/gcc/Inline.html
static inline的時候,如果該函式的指標從未被使用,
那該函式的assembly code就不會被產生(除非有 -fkeep-inline-functions)
但是如果是沒有static的inline(inline或extern inline),
該函式的assembly code一律會被產生。
另一個值得一提的是,
g++會自動把一些member function改成inline(就算沒加inline),
gcc則只有在有最佳化參數才會這麼做。
※ 編輯: wtchen (86.209.24.41), 06/15/2016 19:10:42
> -------------------------------------------------------------------------- <
作者: EdisonX (卡卡獸) 看板: C_and_CPP
標題: Re: [問題] static inline的使用時機
時間: Thu Jun 16 00:58:16 2016
原文後面提到的東西愈來愈多,此篇一點一點慢慢討論,
沒 k 過 spec., 沒追過 assembly code,有誤請不吝指正。
先談 inline 的特性 , 再討論 static , 用大量的程式碼做輔助說明應該會清楚些。
另由於我手邊的 vs2010 要用 inline 就得用 cpp ,故暫時先以 c++ 的方式探討。
以下說的 "DEBUG MODE" 指的是沒有任何優化參數;
"RELEASE MODE" 指的是有下 -O2 優化參數。
壹、inline
的確一般的 inline function,實作和宣告會放在同一個檔案裡面,如下。
例 1.1 (correct)
// mymath.h
#pragma once
inline int inc(int a) { return a+1; }
// main.cpp
#include "mymath.h"
int main() { inc(1) ; return 0;}
若拆成 mymath.h / mymath.cpp 做的話,的確在 compile 時就會有語法上的錯誤
例 1.2 (error)
// mymath.h
#pragma once
inline int inc(int v);
// mymath.cpp
#include "mymath.h"
inline int inc(int v) { return v+1;}
// main.cpp
#include "mymath.h"
int main() { inc(1) ; return 0;}
inline 做的事情是 "建議" compiler 不要實際產生一個 function,
而是如你說的,像是用 macro 的方式在呼叫的地方做替換,但這件事是叫
"建議",而不是 "絕對"。而這個 "建議",通常在 Debug Mode 都是無效的,
這點不論 gcc 或 vs 都一樣。故在 MFC 的一些 src code 會透過 macro 進行
inline switch 作法,也就是在 Debug Mode 時,該函式不實作為 inline 函式;
在 Release Mode 時會實作為 inline 函式,所以 MFC src 會看到另一種副檔
名的 src code : XXXX.inl,讓人感覺很繞路,但會這麼做的原因是想讓
編譯速度加快,不過我沒測過時間就是。
上篇提到的的,compiler 強度可以決定要不要 inline,答案是沒錯的,
甚至我認為說不定未來 inline 關鍵字和 register 關鍵字走向一樣的結果,
在 Debug Mode 的時候基本上是完全不理會,一律用一般 function / variable
方式處理;在 Release Mode 也是忽視這二個識別字,因 user 要不要用
inline 或是 register 的建議,對 compiler 而言可能都很爛,還不如讓
compiler 自己做就好。這也是為什麼 vs release mode 下中斷點,有些變數
看不到、有些函式進不去的原因 (因都被優化掉了)。
貮、static
回到 mymath 問題,但若今天的情況是,我在實作 mymath 的所有對外
functions (像是 mysin, mycos, mytan... 等讓其他 coder 使用的),
並不打算讓其他 coder 在引入 mymath.h 時可以用到 inc 時,我的
inc 就不想放在 mymath.h 裡面,只想實作在 mymath.cpp 裡,且考慮
到 inc 較適合用 inline,情況就變如下
例 2.1
// mymath.h
#pragma once
double mysin(double x);
double mycos(double x);
// mymath.cpp
#include "mymath.h"
inline int inc(int v) { return v+1;}
double mysin(double x){...}
double mycos(double x){...}
一般 "正常使用" , 沒有人在 main.cpp 做 extern 時, Release Mode 下
inc 會被做 inline 沒錯,但若今天有人無聊,想在 main.cpp 中,hack
到 mymath.cpp 裡面的 inc 時,情況就不一樣了。
例 2.2
// main.cpp
#include "mymath.h"
extern int inc(int v);
int main() { inc(1) ; return 0;}
這時由於 inc function 在其他檔案 extern 出來,在 mymath.cpp 裡的 inc
就不會被用 inline 內崁在程式碼裡,但 extern inline 這特性說真的,主要
還是看 compiler 有沒有能力再做 inline。為了避免我原本不想開放、要 inline 的
東西被亂搞,搞成不能變 inline,就在 mymath.cpp 裡的 inc
變成 static inline 修飾,讓其他人沒辦法用 extern 抽出來。如下。
例 2.3
// mymath.h
#pragma once
double mysin(double x);
double mycos(double x);
// mymath.cpp
#include "mymath.h"
static inline int inc(int v) { return v+1;}
double mysin(double x){...}
double mycos(double x){...}
// main.cpp
#include "mymath.h"
extern int inc(int v);
int main() { inc(1) ; return 0;} // 這裡會報 error, 不讓人亂搞
~~~~~~
以上,故事有點長,若敘述有誤,請不吝指正。
謝謝收聽。