久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

如何混合使用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++語言提供了一個混合代碼的機制,使得代碼可以在同一個程序中被兼容的CC++編譯器編譯,。在你移植代碼到不同的平臺和編譯器時,你會體驗到不同的成功度,。本文展示了當你混合使用C,C++時,,如何解決出現的一般的問題。文中所有情況,,展示了使用Sun CC++編譯器時所要做的事情,。(譯注:GCCgccg++也是這一對組合。)


內容
 
 
使用兼容的編譯器


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

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

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

Sun CC++編譯器使用兼容的頭文件,并且使用同樣的C運行時庫,。他們是完全兼容的,。


C++源代碼中訪問C代碼

C++語言提供了一個“鏈接規(guī)范(linkage specification)”,用它你可以聲明函數或對象遵循特定語言的程序鏈接約定,。對象和函數的默認鏈接是C++的,。所有C++編譯器也為兼容的C編譯器提供了C鏈接。

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


聲明鏈接規(guī)范

使用下述標記之一來聲明一個對象或函數具備某種語言language_name的鏈接,。
extern "language_name" declaration ; 
extern "language_name" { declaration ; declaration ; ... }
第一個標記指定了緊隨其后的聲明(或定義)具有語言language_name的鏈接約定,。第二個標記指定花括號內的所有都具有language_name的鏈接。注意,,第二個標記的最后花括號后面不要有分號,。
你可以嵌套鏈接規(guī)范,他們沒有創(chuàng)建一個范圍域(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 
}


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

 

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

如果你想使用一個
C庫,,它定義的頭文件意欲為C編譯器所準備,你可以在extern “C”花括號中包含這個頭文件:

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


Warning-警告-

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

 

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

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

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

 
C structs增加C++特征

假定你想在你的C++代碼中更容易地使用C庫,。并且假定你不使用C風格的訪問方式,,你可能想增加成員函數,或許虛函數,,也可能從class派生等等,。你如何完成這個變換并確保C庫函數仍然能識別你的struct?考慮下面這個例子中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 */

你想把這個struct轉變進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的接口看來更象C++代碼,,并且更容易整合進面向對象風格的編程 ─ 如果它可行的話。

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

C++標準對bufclass mybuf的兼容性沒有任何保證,。這里的代碼,沒有虛函數,,可以工作,,但你不能指望這個。如果你增加了虛函數,,這個代碼會失敗,,因為編譯器增加了額外的數據(比如指向虛表的指針)放在class的開始處。

可移植的方案是把struct buf單獨放著不動它,,盡管你想保護數據成員并僅僅通過成員函數提供訪問,。僅當你不改變聲明的情況下,你才能保證CC++的兼容性,。

你可以從C struct buf派生出一個C++ class mybuf,,并且傳遞指向基類buf的指針給mybuf的成員函數。當轉換mybuf* buf*時,,如果指向mybuf的指針沒有指向buf數據的起始處,C++編譯器會自動調整它,。mybuf的布局在C++編譯時可能發(fā)生改變,,但是操縱mybufbuf對象的C++源代碼將到處都可以工作。下面的例子展示了一個可移植方法給C struct增加C++和面向對象特征:

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對象,,傳遞它們到那些期望buf對象的C代碼,,并且可以結合地很好。當然如果你為mybuf增加了數據成員,,C代碼就不知道它們,。那就是一般類設計的考慮。你要當心要一致性地創(chuàng)建和刪除(deletebufmybuf對象.C代碼刪除(free)一個有C代碼創(chuàng)建的對象是最安全的,,并且不允許C代碼刪除一個mybuf對象,。


C源代碼中訪問C++代碼:

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



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


你可以至多聲明重載集中的一個函數作為extern “C”,,因為一個C函數僅僅可以有一個給定的名字,。如果你要在C中訪問重載函數,你可以以不同的名字寫出C++ wrapper函數,,見下面的例子:

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函數的例子:
int g_int(int); 
double g_double(double);


你也需要包裹(wrapper)函數來調用template functions,,因為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++代碼仍然可以訪問重載函數和template functionsC代碼必須使用wrapper functions,。


C中訪問C++ class


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

假定你有一個C++ class如下:

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

你不能在C代碼中聲明class M,。你能做的最好的事就是傳遞指向class M 對象的指針,這類似于在C標準I/O中傳遞FILE對象,。你可以在C++中寫extern “C”函數訪問class M 對象并在C代碼中調用這些函數,。下面是一個C++函數,被設計來調用成員函數foo:

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

下面是C代碼的一個例子,,它使用了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標準I/O

你可以在
C++程序中使用來自于標準C頭文件<stdio.h>C標準I/O,,因為C標準I/OC++的一部分。
任何關于在同一個程序中混合IOstream和標準I/O的考慮都不依賴于程序是否以明確地包含C代碼,。這個問題對于純粹的C++程序使用標準I/OIOstream是一樣的,。

Sun CC++使用同樣的C運行時庫,,這在關于兼容的編譯器小節(jié)中注明過了,。使用Sun編譯器,,你可以在同一個程序中自由地在CC++代碼中使用標準I/O

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


使用指向函數的指針

指向函數的指針必須指明是否指向一個
C函數或C++函數,,因為CC++函數可能采用不同的調用約定,。否則,編譯器不知道究竟要產生哪種函數調用的代碼,。多數系統(tǒng)對CC++并沒有不同的調用約定,,但是C++允許存在這種可能性。因此你必須在聲明指向函數的指針時要小心,,確保類型匹配,。考慮下面的例子:
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指向一個C++函數,,因為它缺少鏈接說明符,。
Line 2
聲明foo為一個C函數,它具有一個指向C++函數的指針,。
Line 5
試圖用指向g的指針調用foo,,g是一個C函數,所以類型不匹配,。

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

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


使用C++異常



傳播(
Propagating)異常
C函數中調用C++函數,,并且C++函數拋出了一個異常,將會發(fā)生什么,?在是否會使得該異常有適當的行為這個問題上C++標準有些含糊,,并且在一些系統(tǒng)上你不得不采取特別的預防措施。一般而言,,你必須得求諸用戶手冊來確定代碼是否以適當的方式工作,。

Sun C++中不需要預防措施。Sun C++中的異常機制不影響函數調用的方式,。當C++異常被拋出時,,如果一個C函數正處于活動狀態(tài),C函數將轉交給異常處理過程,。

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

許多C++專家相信long_jmp不應該與異常整合,,因為很困難準確地指定它該如何作為,。

如果你在混合有C++C代碼中使用long_jmp,要確保long_jmp不要跨越(cross over)活動的C++函數,。如果你不能確保這點,,查看一下是否你可以通過禁用異常來編譯那個C++代碼。如果對象的析構器被繞過了,,你仍舊可能有問題,。


鏈接程序


某時,多數C++編譯器要求main函數要被C++編譯,。這個要求今天來說并不常見,,Sun C++就不要求這點,。如果你的C++編譯器需要編譯main函數,但你由于某種原因不能這么做,,你可以改變C main函數的名字并從一個C++ main的包裹函數中調用它,。例如,改變C main函數的名字為C_main,,并寫如下C++代碼:

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

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

即使你的程序主要由C代碼構成,,但使用了C++庫,,你需要鏈接C++運行時以支持與C++編譯器提供的庫一起編譯進程序。做這件事的最簡單和最好的方式是使用C++編譯器驅動鏈接過程,。C++編譯器的驅動器知道要鏈接什么庫,,次序如何。特定的庫可以依賴編譯C++代碼時使用的選項,。

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

CC -o myprog main.o f1.o f2.o helper.a
必要的C++運行時庫,,如libCrunlibCstd被自動地鏈接進去。helper.a可能要求使用額外的鏈接選項,。如果你由于某種原因不能使用C++編譯器,,你可以使用CC命令的dryrun選項來獲得編譯器引發(fā)出的命令列表,并把它們捕獲進一個shell腳本,。因為確切的命令(復數)依賴于命令行選項,,你應該復查—dryrun選項輸出和命令行的任何一個變動。


更多信息
 

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



關于作者

Steve Clamage1994年在Sun至今?,。它當前是C++編譯器和Sun ONE Studio編譯器套件的技術領導,。它從1995年開始是ANSI C++委員會的主席。



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

    本站是提供個人知識管理的網絡存儲空間,,所有內容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內容中的聯系方式,、誘導購買等信息,,謹防詐騙,。如發(fā)現有害或侵權內容,請點擊一鍵舉報,。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多