2009-02-26 62 views
22

靜態成員函數和extern「C」鏈接函數有什麼區別?例如,當在C++中使用「makecontext」時,我需要傳遞一個指向函數的指針。 Google建議使用extern「C」鏈接,因爲「makecontext」是C.但是我發現使用靜態作品也是如此。我只是幸運還是...static vs extern「C」/「C++」

class X { 
    public: 
    static void proxy(int i) {} 
} 
makecontext(..., (void (*)(void)) X::proxy, ...); 

VS

extern "C" void proxy(int i) {} 
makecontext(..., (void (*)(void)) proxy, ...); 

編輯:你能告訴編譯器或體系結構,其中的靜態成員的版本不工作(和它不是在編譯器中的錯誤) ?

+0

`我很抱歉,但我仍然不相信......什麼?標準比一些編譯器的巧合實現定義的行爲更具授權的事實? – 2017-03-12 17:23:20

+0

這是一箇舊帖子(8年前今天)我的觀點是當時像恕我直言,如果每個現有的實施不同於標準,那麼也許你應該問是否是標準是錯誤的問題。我正在尋找無法運行的平臺示例。 – 2017-03-14 06:36:40

+0

夠公平的。對於C或C++標準來說,包含所有現有編譯器忽略的意想不到的結果當然是前所未有的。這是我目前最喜歡的:http://stackoverflow.com/a/42335543/2757035但是在這種情況下,我認爲標準很清楚地說明它的含義,並且實現可能隨時改變它們的行爲,如果有的話有一些優勢。 – 2017-03-14 09:46:17

回答

33

是的,你只是幸運:) extern「C」是C語言的一種語言鏈接,每個C++編譯器都必須支持,除了默認的extern「C++」。編譯器可能支持其他語言鏈接。例如GCC支持extern「Java」,它允許與java代碼連接(儘管這很麻煩)。

extern「C」告訴編譯器你的函數可以被C代碼調用。這可以(但不是必須)包括適當的調用約定和適當的C語言名稱(有時稱爲「裝飾」),這取決於實現。如果您有一個靜態成員函數,那麼它的調用約定就是您的C++編譯器之一。它們通常與該平臺的C編譯器相同 - 所以我說你只是幸運。如果你有一個C API,你傳遞一個函數指針,更好地總是把一個與外部的「C」聲明的函數一樣

extern "C" void foo() { ... } 

即使函數指針類型不包含鏈接規範,而是看起來像

void(*)(void) 

的關聯是類的一個組成部分 - 你就不能沒有一個typedef直接表達出來:

extern "C" typedef void(*extern_c_funptr_t)(); 

的科莫C++編譯器,在嚴格模式,例如,如果您嘗試將上面的extern「C」函數的地址分配給(void(*)()),則會發出錯誤,因爲這是指向具有C++鏈接的函數的指針。

4

extern "C"禁用C++編譯器的名稱修改(這是重載所必需的)。

如果你在A.cpp中聲明一個函數爲static,那麼它不能被B.cpp找到(它是從C中剩餘的,並且它具有將函數放入匿名名稱空間中的相同效果)。

+0

這並不回答我的問題 – 2009-02-26 20:09:11

+0

它也可以改變調用約定。 – 2012-02-12 06:06:18

5

請注意,extern C推薦的方式的C/C++互操作性。 Here是大師們在談論它。要添加到eduffy的答案:請注意,全局名稱空間中的靜態函數和變量已棄用。至少使用匿名命名空間。

返回到extern C:如果您不使用extern C,您將必須知道確切的錯位名稱並使用它。這更加痛苦。

2

extern "C"所做的大部分工作主要依賴於編譯器。許多平臺都根據聲明更改了名稱調用和調用約定,但是沒有一個是由標準指定的。標準要求的唯一要求是塊中的代碼可以從C函數中調用。至於你的具體問題,標準說:

Two function types with different language linkages are distinct types even if they are otherwise identical.

這意味着extern "C" void proxy(int i) {}/*extern "C++"*/void proxy(int i) {}有不同的類型,並因此指向這些功能將有不同的類型,以及。編譯器不會失敗你的代碼出於同樣的原因,將不會失敗很大一塊類似工作:

int *foo = (int*)50; 
makecontext(..., (void (*)(void)) foo, ...); 

此代碼可能會在某些平臺上工作,但這並不意味着它會在另一個工作平臺(即使編譯器完全符合標準)。你正在利用你的特定平臺如何工作,如果你不關心編寫可移植代碼,這可能是好的。

至於靜態成員函數,它們不需要有一個this指針,因此編譯器可以自由地將它們視爲非成員函數。再次,這裏的行爲是特定於平臺的。

2

一般來說

存儲類:

存儲類用於指示變量或標識符的持續時間和範圍。

時間:

時間指示變量的壽命。

範圍:

範圍表示變量的可見性。

靜態存儲類:

靜態存儲類用於聲明的標識符是一個局部變量或者函數或一個文件,並存在並且控制傳遞之後,從它那裏保留其值聲明。此存儲類具有永久性的持續時間。這個類聲明的變量從函數的一個調用到下一個調用保留其值。範圍是本地的。一個變量只能由它在內部聲明的函數來知道,或者如果在一個文件中被全局聲明的話,它只能被該文件中的函數知道或看到。這個存儲類保證變量的聲明也將變量初始化爲零或全部關閉。

的extern存儲類:

器extern存儲類用於聲明將是已知的功能在一個文件中並能夠被已知的所有功能在一個程序中的全局變量。此存儲類具有永久性的持續時間。此類的任何變量都會保留其值,直到被另一個賦值更改爲止。範圍是全球性的。變量可以被程序中的所有函數知道或看到。