小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

如何混合使用C和C(下)

 Delores 2007-09-24

Mixing C and C++ Code in the Same Program


By Stephen Clamage, Sun Microsystems, Sun ONE Studio Solaris Tools Development Engineering


Translator: Qiu Longbin <robin.qiu(at)yeah.net>


C++語(yǔ)言提供了一個(gè)混合代碼的機(jī)制,使得代碼可以在同一個(gè)程序中被兼容的CC++編譯器編譯。在你移植代碼到不同的平臺(tái)和編譯器時(shí),你會(huì)體驗(yàn)到不同的成功度。本文展示了當(dāng)你混合使用C,C++時(shí),如何解決出現(xiàn)的一般的問(wèn)題。文中所有情況,展示了使用Sun CC++編譯器時(shí)所要做的事情。(譯注:GCCgccg++也是這一對(duì)組合。)


內(nèi)容
 
 
使用兼容的編譯器


混合代碼的第一個(gè)要求就是你使用的CC++編譯器必須是兼容的。他們必須以同樣的方式,(例如),定義了基本類(lèi)型如int, float或指針。Solaris操作系統(tǒng)指定了C程序的應(yīng)用程序的二進(jìn)制接口(ABI,它包含關(guān)于基本類(lèi)型和函數(shù)如何被調(diào)用的信息。任何Solaris Os上可用的編譯器都必須遵循ABI。

Sun CC++編譯器遵循Solaris OS ABI并且是兼容的。第三方的Solaris OS C編譯器也必須遵循ABI。任何與Solaris Os兼容的C編譯器也同樣與Sun C++編譯器兼容。

被你的C編譯器使用的C運(yùn)行時(shí)庫(kù)也必須同C++編譯器兼容。C++包括了標(biāo)準(zhǔn)C運(yùn)行時(shí)庫(kù)作為其子集,只有稍許不同。如果C++編譯器提供了自己版本的C頭文件,那么這些頭文件的版本被C使用時(shí)也必須是兼容的。

Sun CC++編譯器使用兼容的頭文件,并且使用同樣的C運(yùn)行時(shí)庫(kù)。他們是完全兼容的。


C++源代碼中訪問(wèn)C代碼

C++語(yǔ)言提供了一個(gè)“鏈接規(guī)范(linkage specification)”,用它你可以聲明函數(shù)或?qū)ο笞裱囟ㄕZ(yǔ)言的程序鏈接約定。對(duì)象和函數(shù)的默認(rèn)鏈接是C++的。所有C++編譯器也為兼容的C編譯器提供了C鏈接。

當(dāng)你需要訪問(wèn)一個(gè)用C鏈接編譯的函數(shù)(例如,某個(gè)函數(shù)被C編譯器編譯),就要聲明那個(gè)函數(shù)具備C鏈接(譯注:在C++代碼中)。即使大多數(shù)C++編譯器對(duì)CC++數(shù)據(jù)對(duì)象的鏈接沒(méi)有什么不同,你也需要在你的C++代碼中聲明C數(shù)據(jù)對(duì)象(data objects)具有C鏈接。類(lèi)型(types)沒(méi)有CC++鏈接,除了指向函數(shù)的指針(pointer-to-function)類(lèi)型。


聲明鏈接規(guī)范

使用下述標(biāo)記之一來(lái)聲明一個(gè)對(duì)象或函數(shù)具備某種語(yǔ)言language_name的鏈接。
extern "language_name" declaration ; 
extern "language_name" { declaration ; declaration ; ... }
第一個(gè)標(biāo)記指定了緊隨其后的聲明(或定義)具有語(yǔ)言language_name的鏈接約定。第二個(gè)標(biāo)記指定花括號(hào)內(nèi)的所有都具有language_name的鏈接。注意,第二個(gè)標(biāo)記的最后花括號(hào)后面不要有分號(hào)。
你可以嵌套鏈接規(guī)范,他們沒(méi)有創(chuàng)建一個(gè)范圍域(scope)。考慮下面的例子:
extern "C" {    
     void f(); // C linkage     
     extern "C++" {        
           void g(); // C++ linkage
          extern "C" void h(); // C linkage
          void g2();        // C++ linkage     
     } 
     extern "C++" void k();// C++ linkage     
     void m(); // C linkage 
}


所有上面的函數(shù)都在相同的全局域,盡管是嵌套了鏈接規(guī)范。

 

C++代碼中包含C頭文件

如果你想使用一個(gè)
C庫(kù),它定義的頭文件意欲為C編譯器所準(zhǔn)備,你可以在extern “C”花括號(hào)中包含這個(gè)頭文件:

extern "C" {
     #include "header.h"
 }


Warning-警告-

不用為Solaris OS上的系統(tǒng)頭文件使用該技術(shù)。Solaris頭文件,并且所有賴(lài)于Sun CC++編譯器的頭文件都已經(jīng)為CC++編譯器做好了準(zhǔn)備。如果你指定了一個(gè)鏈接,你可能使得聲明于Solaris頭文件中的聲明失效。

 

創(chuàng)建混合語(yǔ)言(Mixed-Languge)的頭文件

如果你想使得頭文件同時(shí)適合于CC++編譯器,你可能把所有聲明都放置在了extern “C”花括號(hào)中,但是C編譯器并不認(rèn)識(shí)這些語(yǔ)法。每個(gè)C++編譯器都預(yù)定義了宏__cplusplus,這樣你就可以使用這個(gè)宏來(lái)防衛(wèi)C++語(yǔ)法擴(kuò)展:

#ifdef __cplusplus 
extern "C" { 
#endif  ... 
/* body of header */  
 
#ifdef __cplusplus
 } /* closing brace for extern "C" */ 
#endif

 
C structs增加C++特征

假定你想在你的C++代碼中更容易地使用C庫(kù)。并且假定你不使用C風(fēng)格的訪問(wèn)方式,你可能想增加成員函數(shù),或許虛函數(shù),也可能從class派生等等。你如何完成這個(gè)變換并確保C庫(kù)函數(shù)仍然能識(shí)別你的struct?考慮下面這個(gè)例子中Cstruct buf的使用:

/* buf.h */ 
struct buf {     
       char* data; 
       unsigned count; 
}; 
void buf_clear(struct buf*); 
int  buf_print(struct buf*); /* return status, 0 means fail */ 
int  buf_append(struct buf*, const char*, unsigned count); /* same return */

你想把這個(gè)struct轉(zhuǎn)變進(jìn)C++ class,并做下述改變,使它更容易使用:

extern "C" {   
   #include "buf.h" 
} 
class mybuf { // first attempt -- will it work? 
public:    
      mybuf() : data(0), count(0) { }     
      void clear() { buf_clear((buf*)this); }  
      bool print() { return buf_print((buf*)this); } 
      bool append(const char* p, unsigned c) { return buf_append((buf*)this, p, c); }
private:   
      char* data;    
      unsigned count; 
};

class mybuf的接口看來(lái)更象C++代碼,并且更容易整合進(jìn)面向?qū)ο箫L(fēng)格的編程 ─ 如果它可行的話。

當(dāng)成員函數(shù)傳遞this指針給buf函數(shù)時(shí)發(fā)生了什么?C++類(lèi)布局(layout)匹配C布局嗎?this指針指向數(shù)據(jù)成員,還是指向buf?如果增加虛函數(shù)到mybuf又會(huì)如何呢?

C++標(biāo)準(zhǔn)對(duì)bufclass mybuf的兼容性沒(méi)有任何保證。這里的代碼,沒(méi)有虛函數(shù),可以工作,但你不能指望這個(gè)。如果你增加了虛函數(shù),這個(gè)代碼會(huì)失敗,因?yàn)榫幾g器增加了額外的數(shù)據(jù)(比如指向虛表的指針)放在class的開(kāi)始處。

可移植的方案是把struct buf單獨(dú)放著不動(dòng)它,盡管你想保護(hù)數(shù)據(jù)成員并僅僅通過(guò)成員函數(shù)提供訪問(wèn)。僅當(dāng)你不改變聲明的情況下,你才能保證CC++的兼容性。

你可以從C struct buf派生出一個(gè)C++ class mybuf,并且傳遞指向基類(lèi)buf的指針給mybuf的成員函數(shù)。當(dāng)轉(zhuǎn)換mybuf* buf*時(shí),如果指向mybuf的指針沒(méi)有指向buf數(shù)據(jù)的起始處,C++編譯器會(huì)自動(dòng)調(diào)整它。mybuf的布局在C++編譯時(shí)可能發(fā)生改變,但是操縱mybufbuf對(duì)象的C++源代碼將到處都可以工作。下面的例子展示了一個(gè)可移植方法給C struct增加C++和面向?qū)ο筇卣鳎?/font>

extern "C" {   
    #include "buf.h" 
} 
class mybuf : public buf { // a portable solution 
public:
      mybuf() : data(0), count(0) { }
      void clear() { buf_clear(this); }
      bool print() { return buf_print(this); }
     bool append(const char* p, unsigned c) { return buf_append(this, p, c); } 
};


C++代碼可以自由的創(chuàng)建和使用mybuf對(duì)象,傳遞它們到那些期望buf對(duì)象的C代碼,并且可以結(jié)合地很好。當(dāng)然如果你為mybuf增加了數(shù)據(jù)成員,C代碼就不知道它們。那就是一般類(lèi)設(shè)計(jì)的考慮。你要當(dāng)心要一致性地創(chuàng)建和刪除(deletebufmybuf對(duì)象.C代碼刪除(free)一個(gè)有C代碼創(chuàng)建的對(duì)象是最安全的,并且不允許C代碼刪除一個(gè)mybuf對(duì)象。


C源代碼中訪問(wèn)C++代碼:

如果你聲明一個(gè)
C++函數(shù)具有C鏈接,它就可以在由C編譯器編譯的函數(shù)中被調(diào)用。一個(gè)聲明具有C鏈接的函數(shù)可以使用所有C++的特征,如果你想在C代碼中訪問(wèn)它,它的參數(shù)和返回值必須是在C中可訪問(wèn)的。例如,如果一個(gè)函數(shù)聲明有一個(gè)IOstream類(lèi)的引用作為參數(shù),就沒(méi)有(可移植的)方法來(lái)解析這個(gè)參數(shù)類(lèi)型給C編譯器。C語(yǔ)言沒(méi)有引用,模板,或具備C++特征的class.
這里是一個(gè)具備C鏈接的C++函數(shù)的例子:
#include <iostream> 
extern "C" 
int print(int i, double d) 
{     
    std::cout << "i = " << i << ", d = " << d; 
}



你可以在頭文件中聲明一個(gè)函數(shù)print,被CC++代碼共用:
#ifdef __cplusplus
 extern "C" 
#endif  
int print(int i, double d);


你可以至多聲明重載集中的一個(gè)函數(shù)作為extern “C”,因?yàn)橐粋€(gè)C函數(shù)僅僅可以有一個(gè)給定的名字。如果你要在C中訪問(wèn)重載函數(shù),你可以以不同的名字寫(xiě)出C++ wrapper函數(shù),見(jiàn)下面的例子:

int    g(int); 
double g(double); 
extern "C" int g_int(int i){ return g(i); }
 extern "C" double g_double(double d) { return g(d); }
這里是C頭文件wrapper函數(shù)的例子:
int g_int(int); 
double g_double(double);


你也需要包裹(wrapper)函數(shù)來(lái)調(diào)用template functions,因?yàn)?/font>template functions不能聲明為extern “C”:

template<class T> 
T foo(T t) { ... } 
 
extern "C" int  foo_of_int(int t) { return foo(t); } 
extern "C" char* foo_of_charp(char* p) { return foo(p); }

C++代碼仍然可以訪問(wèn)重載函數(shù)和template functions。C代碼必須使用wrapper functions。


C中訪問(wèn)C++ class


能夠從
C代碼中訪問(wèn)C++ class嗎?你可以聲明一個(gè)C struct,看上去象一個(gè)C++ class并能以某種方式調(diào)用成員函數(shù)嗎?答案是肯定的,雖然你必須為維持可移植性增加一些復(fù)雜性。任何對(duì)C++ class的定義的修改都要求你重新審查你的C代碼。

假定你有一個(gè)C++ class如下:

class M { 
public:     
     virtual int foo(int);     // ... 
private:
     int i, j; 
};

你不能在C代碼中聲明class M。你能做的最好的事就是傳遞指向class M 對(duì)象的指針,這類(lèi)似于在C標(biāo)準(zhǔn)I/O中傳遞FILE對(duì)象。你可以在C++中寫(xiě)extern “C”函數(shù)訪問(wèn)class M 對(duì)象并在C代碼中調(diào)用這些函數(shù)。下面是一個(gè)C++函數(shù),被設(shè)計(jì)來(lái)調(diào)用成員函數(shù)foo:

extern "C" int call_M_foo(M* m, int i) { return m->foo(i); }
 

下面是C代碼的一個(gè)例子,它使用了class M:

struct M; /* you can supply only an incomplete declaration */ 
int call_M_foo(struct M*, int); /* declare the wrapper function */ 
int f(struct M* p, int j) /* now you can call M::foo */     
{ 
    return call_M_foo(p, j);
}


混合IOstreamC標(biāo)準(zhǔn)I/O

你可以在
C++程序中使用來(lái)自于標(biāo)準(zhǔn)C頭文件<stdio.h>C標(biāo)準(zhǔn)I/O,因?yàn)?/font>C標(biāo)準(zhǔn)I/OC++的一部分。
任何關(guān)于在同一個(gè)程序中混合IOstream和標(biāo)準(zhǔn)I/O的考慮都不依賴(lài)于程序是否以明確地包含C代碼。這個(gè)問(wèn)題對(duì)于純粹的C++程序使用標(biāo)準(zhǔn)I/OIOstream是一樣的。

Sun CC++使用同樣的C運(yùn)行時(shí)庫(kù),這在關(guān)于兼容的編譯器小節(jié)中注明過(guò)了。使用Sun編譯器,你可以在同一個(gè)程序中自由地在CC++代碼中使用標(biāo)準(zhǔn)I/O。

C++標(biāo)準(zhǔn)說(shuō),你可以在同一個(gè)目標(biāo)“流(stream)”上混合使用標(biāo)準(zhǔn)I/O函數(shù)和IOstream函數(shù),比如標(biāo)準(zhǔn)輸入和輸出流。但是C++實(shí)現(xiàn)在它們的遵從度上可能不同。一些系統(tǒng)要求你在做任何I/O之前先顯式地調(diào)用sync_with_stdio()函數(shù)。在同一個(gè)流或者文件上混合I/O風(fēng)格,實(shí)現(xiàn)品在I/O的效能方面也不同。最差情況下,你得到為每個(gè)字符的輸入輸出做一個(gè)系統(tǒng)調(diào)用的結(jié)果。如果程序有大量的I/O,性能可能是不可接受的。
最安全的途徑是對(duì)任一給定的文件/標(biāo)準(zhǔn)流,堅(jiān)持使用標(biāo)準(zhǔn)I/OIOstream風(fēng)格。在一個(gè)文件或流上使用標(biāo)準(zhǔn)I/O,在另一個(gè)不同的一個(gè)文件或流上使用IOstream,不會(huì)導(dǎo)致任何問(wèn)題。


使用指向函數(shù)的指針

指向函數(shù)的指針必須指明是否指向一個(gè)
C函數(shù)或C++函數(shù),因?yàn)?/font>CC++函數(shù)可能采用不同的調(diào)用約定。否則,編譯器不知道究竟要產(chǎn)生哪種函數(shù)調(diào)用的代碼。多數(shù)系統(tǒng)對(duì)CC++并沒(méi)有不同的調(diào)用約定,但是C++允許存在這種可能性。因此你必須在聲明指向函數(shù)的指針時(shí)要小心,確保類(lèi)型匹配??紤]下面的例子:
typedef int (*pfun)(int); // line 1 
extern "C" void foo(pfun); // line 2 
extern "C" int g(int)     // line 3 ... 
foo( g ); // Error!      // line 5


Line 1聲明了pfun指向一個(gè)C++函數(shù),因?yàn)樗鄙冁溄诱f(shuō)明符。
Line 2
聲明foo為一個(gè)C函數(shù),它具有一個(gè)指向C++函數(shù)的指針。
Line 5
試圖用指向g的指針調(diào)用foo,g是一個(gè)C函數(shù),所以類(lèi)型不匹配。

要確保指向函數(shù)的指針的鏈接規(guī)范與它將要指向的函數(shù)匹配。在下面這個(gè)正確的例子中,所有聲明都包含在extern “C”花括號(hào)中,確保了類(lèi)型匹配。

extern "C" {     
    typedef int (*pfun)(int);
    void foo(pfun);    
    int g(int);
} 
foo( g ); // now OK
指向函數(shù)指針有另外一個(gè)微妙之處,它可能給程序員帶來(lái)陷阱。鏈接規(guī)范應(yīng)用于函數(shù)所有的參數(shù)類(lèi)型和返回類(lèi)型上。如果你將一個(gè)詳細(xì)聲明的指向函數(shù)的指針(pointer-to-function)用作函數(shù)參數(shù),鏈接規(guī)范也同樣地作用在了這個(gè)指向函數(shù)的指針上。如果你通過(guò)使用typedef聲明了一個(gè)指向函數(shù)的指針,這個(gè)typedef類(lèi)型在用于函數(shù)聲明中時(shí),鏈接規(guī)范不會(huì)受到影響。例如,考慮下面的代碼:
typedef int (*pfn)(int); 
extern "C" void foo(pfn p) { ... }   // definition 
extern "C" void foo( int (*)(int) ); // declaration
前兩行可以出現(xiàn)在一個(gè)程序文件中,第三行可以出現(xiàn)在一個(gè)頭文件中,在此頭文件中你不想暴露出內(nèi)部使用的typedef的名字。盡管你想讓foo的聲明和定義相匹配,但它們不匹配。foo的定義接受一個(gè)指向C++函數(shù)的指針,但是其聲明接受一個(gè)指向C函數(shù)的指針。這段代碼聲明了一對(duì)重載函數(shù)。(譯注:在此是參數(shù)類(lèi)型的鏈接規(guī)范不同。)
為了避免這個(gè)問(wèn)題,得在聲明中一致地使用typedefs,或者以某個(gè)適當(dāng)?shù)逆溄右?guī)范包圍typedefs。例如,假定你想讓foo接受一個(gè)指向C函數(shù)的指針,你可以以下面的方式寫(xiě)出foo的定義:
extern "C" {    
    typedef int (*pfn)(int);  
    void foo(pfn p) { ... }
 }


使用C++異常



傳播(
Propagating)異常
C函數(shù)中調(diào)用C++函數(shù),并且C++函數(shù)拋出了一個(gè)異常,將會(huì)發(fā)生什么?在是否會(huì)使得該異常有適當(dāng)?shù)男袨檫@個(gè)問(wèn)題上C++標(biāo)準(zhǔn)有些含糊,并且在一些系統(tǒng)上你不得不采取特別的預(yù)防措施。一般而言,你必須得求諸用戶(hù)手冊(cè)來(lái)確定代碼是否以適當(dāng)?shù)姆绞焦ぷ鳌?/font>

Sun C++中不需要預(yù)防措施。Sun C++中的異常機(jī)制不影響函數(shù)調(diào)用的方式。當(dāng)C++異常被拋出時(shí),如果一個(gè)C函數(shù)正處于活動(dòng)狀態(tài),C函數(shù)將轉(zhuǎn)交給異常處理過(guò)程。

混合異常和set_jmp,long_jmp
最好的建議是在包含C++代碼的程序中不要使用long_jmpC++異常機(jī)制和C++關(guān)于銷(xiāo)毀超出作用域?qū)ο蟮囊?guī)則可能被long_jmp違反,從而得到不確定的結(jié)果。一些編譯器整合了異常和long_jmp,允許它們協(xié)同工作,但你不能依賴(lài)這樣的行為。Sun C++使用與C編譯器相同的set_jmplong_jmp。

許多C++專(zhuān)家相信long_jmp不應(yīng)該與異常整合,因?yàn)楹芾щy準(zhǔn)確地指定它該如何作為。

如果你在混合有C++C代碼中使用long_jmp,要確保long_jmp不要跨越(cross over)活動(dòng)的C++函數(shù)。如果你不能確保這點(diǎn),查看一下是否你可以通過(guò)禁用異常來(lái)編譯那個(gè)C++代碼。如果對(duì)象的析構(gòu)器被繞過(guò)了,你仍舊可能有問(wèn)題。


鏈接程序


某時(shí),多數(shù)C++編譯器要求main函數(shù)要被C++編譯。這個(gè)要求今天來(lái)說(shuō)并不常見(jiàn),Sun C++就不要求這點(diǎn)。如果你的C++編譯器需要編譯main函數(shù),但你由于某種原因不能這么做,你可以改變C main函數(shù)的名字并從一個(gè)C++ main的包裹函數(shù)中調(diào)用它。例如,改變C main函數(shù)的名字為C_main,并寫(xiě)如下C++代碼:

extern "C" int C_main(int, char**); // not needed for Sun C++ 
int main(int argc, char** argv) { return C_main(argc, argv); }

當(dāng)然,C_main必須是被聲明在C代碼中,并返回一個(gè)int。如上注解,使用Sun C++是不會(huì)有這個(gè)麻煩。

即使你的程序主要由C代碼構(gòu)成,但使用了C++庫(kù),你需要鏈接C++運(yùn)行時(shí)以支持與C++編譯器提供的庫(kù)一起編譯進(jìn)程序。做這件事的最簡(jiǎn)單和最好的方式是使用C++編譯器驅(qū)動(dòng)鏈接過(guò)程。C++編譯器的驅(qū)動(dòng)器知道要鏈接什么庫(kù),次序如何。特定的庫(kù)可以依賴(lài)編譯C++代碼時(shí)使用的選項(xiàng)。

假定你有C程序文件main.o, f1.of2.o,你可以使用C++程序庫(kù)helper.a。用Sun C++,你要如下引發(fā)命令行:

CC -o myprog main.o f1.o f2.o helper.a
必要的C++運(yùn)行時(shí)庫(kù),如libCrunlibCstd被自動(dòng)地鏈接進(jìn)去。helper.a可能要求使用額外的鏈接選項(xiàng)。如果你由于某種原因不能使用C++編譯器,你可以使用CC命令的dryrun選項(xiàng)來(lái)獲得編譯器引發(fā)出的命令列表,并把它們捕獲進(jìn)一個(gè)shell腳本。因?yàn)榇_切的命令(復(fù)數(shù))依賴(lài)于命令行選項(xiàng),你應(yīng)該復(fù)查—dryrun選項(xiàng)輸出和命令行的任何一個(gè)變動(dòng)。


更多信息
 

Sun ONE Studio C/C++ Documentation
 Sun ONE C
C++編譯器的最新信息,包括man手冊(cè)頁(yè)和readme文件。



關(guān)于作者

Steve Clamage1994年在Sun至今?。它當(dāng)前是C++編譯器和Sun ONE Studio編譯器套件的技術(shù)領(lǐng)導(dǎo)。它從1995年開(kāi)始是ANSI C++委員會(huì)的主席。



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=626342

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多