2009-12-15 44 views
12

我遇到了一個可以工作(使用XLC8和MSFT9編譯器)的代碼片斷,其中包含一個C++文件,其中包含一個使用C鏈接和引用參數定義的函數。這讓我感到不安,因爲引用僅僅是C++。所討論的函數是從C代碼中調用的,在C代碼中聲明瞭一個指針參數來代替參考參數。C++引用參數和C鏈接

簡化的例子

C++文件

extern "C" void f(int &i) 
{ 
    i++; 
} 

C文件

void f(int *); 

int main() 
{ 
    int a = 2; 
    f(&a); 
    printf("%d\n", a); /* Prints 3 */ 
} 

現在,街道上的字是大多數C++編譯器,在引擎蓋下,像指針一樣實現引用。它是這樣的,只是純粹的運氣這個代碼的工作原理,或者說它在C++規範中的某處說什麼當你用一個引用參數和C鏈接定義一個函數時結果是什麼?我一直無法找到關於此的任何信息。

+0

從我所看到的第7.5節C++標準(鏈接規範)對此沒有任何說明。 – 2009-12-15 09:08:32

回答

4

我的n3000.pdf(來自here)的副本在第7節有說明。5種— 聯動規格

。從C++到 其他語言中定義的對象與C++中其他語言定義的 對象之間的鏈接是 實現定義的和 語言相關的。只有在兩個 語言實現的對象佈局策略類似 足夠的情況下才可以實現這種連接。

由於C和C++是不同的語言,這意味着你不能依賴於這個普通編譯器的「特性」。

更強是音符5中相同的部分(重點煤礦):

如果兩個聲明聲明函數 具有相同的名稱和參數 型列表(8.3.5)至是 成員相同的名稱空間或 聲明與 具有相同名稱的對象是相同名稱空間的成員,並且 聲明給出名稱 不同的語言鏈接, 程序不合格; 如果聲明在不同翻譯單元中出現 ,則不需要診斷 。

所以,我會說,你做了什麼,不能保證按照標準工作,並且不需要編譯器打印診斷爲你給出的例子,因爲的聲明是在不同的翻譯單元

僅供參考,它適用於Snow Leopard上的gcc和g ++版本4.2.1。

+0

完全正確的報價,但請注意,它也有一個好處:因爲它的實施定義無論如何,如果它的工作有效。 – MSalters 2009-12-15 10:31:58

+0

是的,但也許它適用,因爲它適用於特定版本的編譯器?我試着查看gcc文檔,但找不到任何信息。據我瞭解,問題更多的是一個「理論」問題,試圖找出標準是否允許這樣的聲明。我懷疑OP是否想用真實的代碼來做這樣的事情,因爲避免這麼做很容易。 – 2009-12-15 10:42:01

+0

那麼代碼在生產和工作,但我想保持代碼標準兼容,因爲新的平臺或編譯器可能在未來的目標。 – olovb 2009-12-15 11:50:14

11

在許多情況下,但不是全部,引用可以用'自動解除引用'指針來實現。任何特定的編譯器都以這種方式使用C鏈接和引用參數來對待函數,這在C++標準中是不能保證的,您應該將其視爲實現細節。

這並不難寫的轉發功能,需要一個指針,並調用你的函數,如果你需要做到這一點,而不依賴於實現細節:

void real_f(int& n) { 
    n++; 
} 
extern "C" void f(int* p) { // called from C code 
    real_f(*p); 
} 
3

一個參考是別名爲一個對象。從技術上講,C中沒有任何東西可以直接映射到C++引用。

引用的明顯實現是作爲每當使用它時解除引用的(常量)指針。所以,根據你的編譯器如何實現引用,你的代碼可能會工作。但這是不正確的。

解決方案是編寫一個C函數,它接收一個真實對象或指向該對象的指針並從中調用C++函數。

+1

這不是保證,但它不一定是不正確的。其他語言如何調用C++函數(包括'extern「C」'C++函數)不在C++標準的範圍內。就C而言,函數已被正確地聲明和調用。這是一個實施問題,您是否應該或不應該依賴它將因項目要求而異。 – 2009-12-15 09:14:00

0

這就是Bjarne的Stroustrup的有什麼看法引用:

大多數引用工具使用指針變量來實現,它的引用通常佔用內存的一個字。然而,參考該純粹在本地使用,並且通常由優化器「消除」。

例如:

struct S 
{ 
    int a; 
    int b[100]; 
}; // just an example 

void do_something(const vector<S>& v) 
{ 
    for (int i=0; i<v.size(); ++i) 
    { 
     int(&p)[100] = v[i].b; 
     for (int j=0; j<100; ++j) 
      cout <<p[j]; 
    } 
} 

在這種情況下,對不需要被存儲在存儲器(可能只是存在於一個寄存器,也許它消失在指令)。

+0

你的意思是'int(&p)[100] = v [i] .b;';還有s /,/; /和v.size()。 – 2009-12-15 09:19:12

+0

@Roger:謝謝,我編輯了我的帖子:) – 2009-12-15 12:16:03

+4

如果你給出了更完整的引用,這將是很好的。 * Stroustrup在哪裏說的? (網站或書名,版本和頁碼)另一方面,我們*已經知道*引用常常被實現爲指針。這就是爲什麼問題中的代碼和現在一樣。我沒有看到你試圖用這個答案做出的觀點,特別是關於C鏈接函數的參數。 – 2009-12-15 14:24:55

1

我想你已經有了一些很好的答案,所以只是指出一些額外的問題。

我的卑微的理解是,extern只是創建一個C符號。

所以你的榜樣還是「似乎工作」(GCC/G ++),甚至當我添加一個類的對象 - 我不得不嘗試IR相信這一點:

class A {}; 

extern "C" void f(int &i, A a) { 
    i++; 
}