久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 2387|回復: 0
收起左側

同一程序中混合調用C和C++代碼

[復制鏈接]
ID:105323 發表于 2016-2-22 00:24 | 顯示全部樓層 |閱讀模式
// 覺得這篇文章寫的還可以,比較詳細有點學究的味道,所以就翻譯過來。C++和C混合編碼雖然不難理解,但C庫、C++庫、extern "C"、extern "C++"、#inlcude <stdio.h>、#include <CStdio>等等,區別起來也有點困難。發生誤解的根源在于沒有把編譯和連接理解透徹。一個程序使用了某個函數,不管該函數是在某個頭文件中定義的函數,還是通過extern定義的外部函數,還是本地已經定義好的函數,該函數都要經過編譯、連接兩個步驟。在編譯階段,C++編譯器會根據函數返回類型、參數類型等,進行函數名修飾;之后才會根據修飾后的函數名,進行連接。(注意函數名修飾發生在編譯階段)。因此,在定義可同時被C、C++使用的頭文件時,要考慮到C、C++編譯器的編譯過程,綜合使用extern "C"、#ifdef __cplusplus(所有C++編譯器都會預定義這個頭文件)來聲明該頭文件。
// 本文中:源代碼(Source),程序(Program)是指未編譯的程序;代碼(Code)應該指的是頭文件(.H)加庫(.LIB / .DLL)的組合。
C++語言提供了這種機制:它允許在同一個程序中有“C編譯器”和“C++編譯器”編譯的代碼(程序庫)混合存在。本文主要解決由于C和C++代碼混合使用所引起的一些通用問題,同時注明了幾個容易引起的誤區。
主要內容
-使用可兼容的編譯器
-C++源程序中調用C代碼
-C源程序中調用C++代碼
-混合IOstream和C標準I/O         
-函數指針的處理
-C++異常的處理
-程序的連接

1. 使用可兼容的編譯器

本文的討論建立在這樣的基礎上:所使用C和C++編譯器是兼容的;它們都以同種方式定義int、float、pointer等數據類型。
C編譯器所使用的C運行時庫也要和C++編譯器兼容。C++包含了C運行時庫,視為它的一個子集。如果C++編譯器提供它自己的C版本頭文件,這些頭文件也要和C編譯器兼容。

2. 從C++源程序中調用C代碼

C++語言為了支持重載,提供了一種連接時的“函數名修飾”。對C++文件(.CPP)文件的編譯、連接,缺省采用的是這種“C++的方式”,但是所有C++編譯器都支持“C連接”(無函數名修飾)。
當需要調用“C連接”(由C編譯器編譯得到的)時,即便幾乎所有C++編譯器對“數據”的“連接修飾”與C編譯器無任何差異,但還是應該在C++代碼中聲明“C連接”;指向函數的指針沒有“C連接”或“C++連接”。

能夠對“連接修飾”進行嵌套,如下,這樣不會創建一個scope,所有函數都處于同一個全局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
}

如果使用C庫及其對應的.H頭文件,往往可以這樣做:
   
extern "C" {
    #include "header.h";
}

建立支持多語言的.H頭文件,如同時支持C和C++的頭文件時,需要把所有的聲明放在extern "C"的大括號里頭,但是“C編譯器”卻不支持 " extern "C" "這種語法。每一個C++編譯器都會預定義__cplusplus宏,可以用這個宏確保C++的語法擴展。
   
#ifdef __cplusplus
extern "C" {
#endif
    /* body of header */
#ifdef __cplusplus
}
#endif

假如想在C++代碼中更加方便的使用C庫,例如在C++類的成員函數/虛函數中使用"C庫",怎樣確保"C庫"中的函數能夠正確識別出"C++"的類?利用extern "C"可以這樣做:

struct buf {
    char* data;
    unsigned count;
};
void buf_clear(struct buf*);
int buf_print(struct buf*);
int buf_append(struct buf*, const char*, unsigned count);

在C++中可以方便的使用這個結構,如下:

extern "C" {
    #include "buf.h";
}
class mybuf {
public:
    mybuf() : data(0), count(0) {}
    void clear() { buf_clear((buf*)this); }
    bool print() { return buf_print((buf*)this); }
    bool append()...
private:
    char* data;
    unsigned count;                 
} ;

提供給class mybuf的接口看起來更像C++的Code,它能夠更加容易的被集成到面向對象編程中。但是,這個例子是在沒有虛函數、且類的數據區開頭沒有冗余數據的情況下。

另一個可供替代的方案是,保持struct buf的獨立性,而從其派生出C++的類。當傳遞指針到struct buf的成員函數時,即使指向mybuf的指針數據與struct buf位置不完全吻合,C++編譯器也會自動調整,把類的類型協變到struct buf。class mybuf的layout可能會隨不同的C++編譯器而不同,但是這段操作mybuf和buf的C++源代碼也能到哪里都工作。如下是這種派生的源代碼,它也隱含了struct結構具有的面向對象的特性:

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++代碼能夠自由地使用mybuf類,傳遞自身到struct buf的C代碼中,能很好的工作,當然,如果給mybuf加入了別的成員變量,C代碼是不知道的。這是“派生類”的一種常規設計思路。

3. 從C源代碼中調用C++代碼

如果聲明C++函數采用“C連接”,那么它就能夠被"C代碼"引用,前提是這個函數的參數和返回值必須能夠被"C代碼"所接受。如果該函數接受一個IOStream的類作為參數,那么C將不能使用,因為C編譯器沒有沒有C++的這個模板庫。下面是一個C++函數采用“C連接”的例子:

#include <iostream>
extern "C" int print(int i, double d)
{
    std::cout << "i = " << i << ", d = " << d;
}

可以這樣定義一個能同時被C和C++使用的頭文件:

#ifdef __cplusplus
extern "C"
#endif
int print(int i, double d);

對于C++同名重載函數,利用extern "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); }

wrapper的頭文件可以這樣寫:

int g_int(int);
double g_double(double);

模板函數不能用extern "C"修飾,也可以采取wrapper的方式,如下:

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); }

4. 從C代碼中訪問C++的類

能否聲明一個類似與C++類的Struct,從而調用其成員函數,達到C代碼訪問C++類的目的呢?答案是可以的,但是,為了保持可移植性,必須要加入一個兼容的措施。修改C++類時,也要考慮到調用它的C代碼。加入有一個C++類如下:

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

在C代碼中無法聲明Class M,最好的方式是采用指針。C++代碼中聲明如下:

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

在C代碼中,可以這樣調用:

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); }

5. 混合IOstream和C標準I/O

C++程序中,可以通過C標準頭文件<stdio.h>使用C標準I/O,因為C標準I/O是C++的一部分。程序中混合使用IOstream和標準I/O與程序是否含有C代碼沒有必然聯系。
C++標準說可以在同一個目標stream上混合C標準I/O和IOstream流,例如標注輸入流、標準輸出流,這一點不同的C++編譯器實現卻不盡相同,有的系統要求用戶在進行I/O操作前顯式地調用sync_with_stdio()。其它還有程序調用性能方面的考慮。

6. 如何使用函數指針

必須確定一個函數指針究竟是指向C函數還是C++函數。因為C和C++函數采用不同的調用約定。如果不明確指針究竟是C函數還是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

第一行聲明一個C++函數指針(因為沒有link specifier);
第二行聲明foo是一個C函數,但是它接受一個C++函數指針;
第三行聲明g是一個C函數;
第五行出現類型不匹配;

解決這個問題可以如下:

extern "C" {
    typedef int (*pfun)(int);
    void foo(pfun);
    int g(int);
}
foo( g ); // now OK

當把linkage specification作用于函數參數或返回值類型時,函數指針還有一個難以掌握的誤區。當在函數參數聲明中嵌入一個函數指針的聲明時,作用于函數聲明的linkage specification 也會作用到這個函數指針的聲明中。如果用typedef聲明的函數指針,那么這個聲明可能會失去效果,如下:

typedef int (*pfn)(int);
extern "C" void foo(pfn p) { ... }     // definition
extern "C" void foo( int (*)(int) );   // declaration

假定前兩行出現在源程序中。
第三行出現在頭文件中,因為不想輸出一個私有定義的typedef。盡管這樣做的目的是為了使函數聲明和定義吻合,但結果卻是相反的。foo的定義是接受一個C++的函數的指針,而foo的聲明卻是接受一個C函數指針,這樣就構成兩個同名函數的重載。為了避免這種情況,應該使typedef緊靠函數聲明。例如,如果想聲明foo接受一個C函數指針,可以這樣定義:

extern "C" {
    typedef int (*pfn)(int);
    void foo(pfn p) { ... }
};

7. 處理C++異常

C函數調用C++函數時,如果C++函數拋出異常,應該怎樣解決呢?可以在C程序使用用long_jmp處理,只要確信long_jmp的跳轉范圍,或者直接把C++函數編譯成不拋出異常的形式。

8. 程序的連接

過去大部分C++編譯器要求把main()編譯到程序中,目前這個需求已經不太普遍。如果還需要,可以通過更改C程序的main函數名,在C++通過wrapper的方式調用。例如,把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型的函數。

回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: av在线资源网 | 国产com | 日韩精品二区 | 国产成人99久久亚洲综合精品 | 亚洲人在线 | 国产成人一区二区三区 | 香蕉视频免费 | 国产剧情在线 | 在线亚洲天堂 | 色草在线| 亚洲日本精品 | 在线观看av免费 | 婷婷激情五月 | 五月亚洲 | 日日夜夜av | 午夜在线观看视频网站 | 中文字幕在线观看网址 | 成人免费黄色大片 | 中文字幕免费观看 | 国产精品一区二区在线免费观看 | 欧美成人一区二区三区 | www.麻豆av| 亚洲视频一区二区 | 中文字幕在线观看一区二区三区 | 亚洲午夜在线 | 国产日韩综合 | av黄色在线观看 | 青青草精品视频 | 色婷婷影视 | 欧美日韩在线不卡 | 免费一看一级毛片 | 日本色综合 | 日韩久久网 | 日韩视频一区二区三区 | 精品一区二区三区三区 | 日本激情网 | 久久综合国产 | 欧美高清视频在线观看mv | 婷婷亚洲综合 | 精品在线免费视频 | 中文字幕日韩一区 |