GCC編譯C C++ 和C混合C++

C++ 編程中相關文件後綴
.a 靜態庫 (archive)
.C
.c
.cc
.cp
.cpp
.cxx
.c++
C++源代碼(需要編譯預處理)
.h C或者C++源代碼頭文件
.ii C++源代碼(不需編譯預處理)
.o 對象文件
.s 彙編語言代碼
.so 動態庫
<none> 標準C++系統頭文件


    單個源文件生成可執行程序
    下面是一個保存在文件 helloworld.cpp 中一個簡單的 C++ 程序的代碼:
    點擊(此處)摺疊或打開
    /* helloworld.cpp */
    #include <iostream>
    int main(int argc,char *argv[])
    {
    std::cout 《 "hello, world"《 std::endl;
    return(0);
    }
    程序使用定義在頭文件 iostream 中的 cout,向標準輸出寫入一個簡單的字符串。該代碼可用以下命令編譯爲可執行文件:
    $ g++ helloworld.cpp
    編譯器 g++ 通過檢查命令行中指定的文件的後綴名可識別其爲 C++ 源代碼文件。編譯器默認的動作:編譯源代碼文件生成對象文件(object file),鏈接對象文件和 libstdc++ 庫中的函數得到可執行程序。然後刪除對象文件。由於命令行中未指定可執行程序的文件名,編譯器採用默認的 a.out。華夏名網程序可以這樣來運行:
    $ ./a.out hello, world
    更普遍的做法是通過 -o 選項指定可執行程序的文件名。下面的命令將產生名爲 helloworld 的可執行文件:
    $ g++ helloworld.cpp -o helloworld
    在命令行中輸入程序名可使之運行:
    $ ./helloworld hello, world
    程序 g++ 是將 gcc 默認語言設爲 C++ 的一個特殊的版本,鏈接時它自動使用 C++ 標準庫而不用 C 標準庫。通過遵循源碼的命名規範並指定對應庫的名字,用 gcc 來編譯鏈接 C++ 程序是可行的,如下例所示:
    $ gcc helloworld.cpp -lstdc++ -o helloworld
    選項 -l (ell) 通過添加前綴 lib 和後綴 .a 將跟隨它的名字變換爲庫的名字 libstdc++.a。而後它在標準庫路徑中查找該庫。gcc 的編譯過程和輸出文件與 g++ 是完全相同的。
    在大多數系統中,GCC 安裝時會安裝一名爲 c++ 的程序。如果被安裝,它和 g++ 是等同,如下例所示,用法也一致:
    $ c++ helloworld.cpp -o helloworld []多個源文件生成可執行程序
    如果多於一個的源碼文件在 g++ 命令中指定,它們都將被編譯並被鏈接成一個單一的可執行文件。下面是一個名爲 speak.h 的頭文件;它包含一個僅含有一個函數的類的定義:
    點擊(此處)摺疊或打開
    /* speak.h */
    #include <iostream>
    class Speak {
    public: void sayHello(const char *);
    };
    下面列出的是文件 speak.cpp 的內容:包含 sayHello() 函數的函數體:
    點擊(此處)摺疊或打開
    /* speak.cpp */
    #include "speak.h"
    void Speak::sayHello(const char *str)
    {
    std::cout 《 "Hello " 《 str 《 "\n";
    }
    文件 hellospeak.cpp 內是一個使用 Speak 類的程序:
    點擊(此處)摺疊或打開
    /* hellospeak.cpp */
    #include "speak.h"
    int main(int argc,char *argv[])
    {
    Speak speak;
    speak.sayHello("world");
    return(0);
    }
    下面這條命令將上述兩個源碼文件編譯鏈接成一個單一的可執行程序:
    $ g++ hellospeak.cpp speak.cpp -o hellospeak
    PS:這裏說一下爲什麼在命令中沒有提到“speak.h“該文件(原因是:在“speak.cpp“中包含有”#include"speak.h"“這句代碼,它的意思是搜索系統頭文件目錄之前將先在當前目錄中搜索文件“speak.h“。而”speak.h“正在該目錄中,不用再在命令中指定了)。
    [編輯]源文件生成對象文件
    選項 -c 用來告訴編譯器編譯源代碼但不要執行鏈接,輸出結果爲對象文件。文件默認名與源碼文件名相同,只是將其後綴變爲 .o。例如,下面的命令將編譯源碼文件 hellospeak.cpp 並生成對象文件 hellospeak.o:
    $ g++ -c hellospeak.cpp
    命令 g++ 也能識別 .o 文件並將其作爲輸入文件傳遞給鏈接器。下列命令將編譯源碼文件爲對象文件並將其鏈接成單一的可執行程序:
    $ g++ -c hellospeak.cpp
    $ g++ -c speak.cpp
    $ g++ hellospeak.o speak.o -o hellospeak
    選項 -o 不僅僅能用來命名可執行文件。它也用來命名編譯器輸出的其他文件。例如:除了中間的對象文件有不同的名字外,下列命令生將生成和上面完全相同的可執行文件:
    $ g++ -c hellospeak.cpp -o hspk1.o
    $ g++ -c speak.cpp -o hspk2.o
    $ g++ hspk1.o hspk2.o -o hellospeak [編輯]編譯預處理
    選項 -E 使 g++ 將源代碼用編譯預處理器處理後不再執行其他動作。下面的命令預處理源碼文件 helloworld.cpp 並將結果顯示在標準輸出中:
    $ g++ -E helloworld.cpp

本文前面所列出的 helloworld.cpp 的源代碼,僅僅有六行,而且該程序除了顯示一行文字外什麼都不做,但是,預處理後的版本將超過 1200 行。這主要是因爲頭文件 iostream 被包含進來,而且它又包含了其他的頭文件,除此之外,還有若干個處理輸入和輸出的類的定義。

    預處理過的文件的 GCC 後綴爲 .ii,它可以通過 -o 選項來生成,例如:
    $ gcc -E helloworld.cpp -o helloworld.ii [編輯]生成彙編代碼
    選項 -S 指示編譯器將程序編譯成彙編語言,輸出彙編語言代碼而後結束。下面的命令將由 C++ 源碼文件生成彙編語言文件 helloworld.s:
    $ g++ -S helloworld.cpp
    生成的彙編語言依賴於編譯器的目標平臺。
    [編輯]創建靜態庫
    靜態庫是編譯器生成的一系列對象文件的集合。鏈接一個程序時用庫中的對象文件還是目錄中的對象文件都是一樣的。庫中的成員包括普通函數,類定義,類的對象實例等等。靜態庫的另一個名字叫歸檔文件(archive),管理這種歸檔文件的工具叫 ar 。
    在下面的例子中,我們先創建兩個對象模塊,然後用其生成靜態庫。
    頭文件 say.h 包含函數 sayHello() 的原型和類 Say 的定義:
    點擊(此處)摺疊或打開
    /* say.h */
    #include <iostream>
    void sayhello(void);
    class Say
    {
    private: char *string;
    public: Say(char *str)
    {
    string = str;
    }
    void sayThis(const char *str)
    {
    std::cout 《 str 《 " from a static library\n";
    }
    void sayString(void);
    };
    下面是文件 say.cpp 是我們要加入到靜態庫中的兩個對象文件之一的源碼。它包含 Say 類中 sayString() 函數的定義體;類 Say 的一個實例 librarysay 的聲明也包含在內:
    點擊(此處)摺疊或打開
    /* say.cpp */
    #include "say.h"
    void Say::sayString()
    {
    std::cout 《 string 《 "\n";
    }
    Say librarysay("Library instance of Say");
    源碼文件 sayhello.cpp 是我們要加入到靜態庫中的第二個對象文件的源碼。它包含函數 sayhello() 的定義:
    點擊(此處)摺疊或打開
    /* sayhello.cpp */
    #include "say.h"
    void sayhello()
    {
    std::cout 《 "hello from a static library\n";
    }
    下面的命令序列將源碼文件編譯成對象文件,命令 ar 將其存進庫中:$ g++ -c sayhello.cpp $ g++ -c say.cpp $ ar -r libsay.a sayhello.o say.o
    程序 ar 配合參數 -r 創建一個新庫 libsay.a 並將命令行中列出的對象文件插入。採用這種方法,如果庫不存在的話,參數 -r 將創建一個新的庫,而如果庫存在的話,將用新的模塊替換原來的模塊。
    下面是主程序 saymain.cpp,它調用庫 libsay.a 中的代碼:
    點擊(此處)摺疊或打開
    /* saymain.cpp */
    #include "say.h"
    int main(int argc,char *argv[])
    {
    extern Say librarysay;
    Say localsay = Say("Local instance of Say");
    sayhello(); librarysay.sayThis("howdy");
    librarysay.sayString();
    localsay.sayString();
    return(0);
    }
    該程序可以下面的命令來編譯和鏈接:
    $ g++ saymain.cpp libsay.a -o saymain
    程序運行時,產生以下輸出:
    hello from a static library howdy from a static library Library instance of Say Local instance of Say
    C與C++混合編程
    C++ 是在 C 語言的基礎上發展起來的。在某種程度上,我們可將 C++ 看做 C 的一種擴展。在本質上,二者的數據類型和函數調用慣例都是一致的,因此 C 與 C++ 混合編譯也是很自然的事情。
    二者的區別僅在於編譯後函數的名字不同──C 簡單地使用函數名而不考慮參數的個數或類型,而 C++ 編譯後的函數名則總是將參數類型列表作爲其一部分。儘管如此,C++ 提供了特殊的機制來聲明 C 函數,這意味着一個 C++ 程序可以直接聲明和調用 C 函數。
    [編輯]C++調用C函數
    下面是 C++ 程序調用 C 函數 csayhello() 的一個例子。由於該函數在 C++ 程序內聲明時使用了 extern "C",故調用可以直接進行:
    點擊(此處)摺疊或打開
    /* cpp2c.cpp */
    #include <iostream>
    extern "C" void csayhello(char *str);
    int main(int argc,char *argv[])
    {
    csayhello("Hello from cpp to c");
    return(0);
    }
    C 函數不需任何特殊處理,其代碼如下:
    點擊(此處)摺疊或打開
    /* csayhello.c */
    #include <stdio.h>
    void csayhello(char *str)
    {
    printf("%s\n",str);
    }
    下面三條命令編譯以上兩個文件並將二者鏈接爲一個可執行文件。由於 gcc 和 g++ 的靈活性使得存在很多方法來完成該任務,但這三條命令或許是最常用的:
    $ g++ -c cpp2c.cpp -o cpp2c.o
    $ gcc -c csayhello.c -o csayhello.o
    $ gcc cpp2c.o csayhello.o -lstdc++ -o cpp2c
    注意到,在最後鏈接的時候指定 C++ 標準庫是必須的,這是因爲我們用的是 gcc 而不是 g++ 調用的鏈接器。如果使用的是 g++ 的話,C++ 標準庫默認會被鏈接。
    最普遍的做法是,將函數聲明放到頭文件中,然後將所有內容包含在 extern "C" 聲明塊內。文件內容像下面所示:
    點擊(此處)摺疊或打開
    extern "C" {
    int mlimitav(int lowend, int highend);
    void updatedesc(char *newdesc);
    double getpct(char *name);
    };
    [編輯]C調用C++函數
    要使 C 程序能夠調用 C++ 中函數的話,C++ 提供一個符合 C 調用慣例的函數是必須的。下面的例子演示了在 C++ 內創建 C 函數的語法:
    點擊(此處)摺疊或打開
    /* cppsayhello.cpp */
    #include <iostream>
    extern "C" void cppsayhello(char *str);
    void cppsayhello(char *str)
    {
    std::cout 《 str 《 "\n";
    }
    儘管函數 cppsayhello() 通過 extern "C" 聲明爲 C 函數,事實上它是 C++ 源代碼的一部分,這意味着函數體內是真正的 C++ 代碼。在函數內你可以自由地創建和析構對象。如果你要在 cppsayhello() 內調用 C 函數的話,將其聲明爲 extern "C" 是必須的。否則,編譯器會將作爲一個 C++ 函數並相應地更改函數名。
    下面是調用 C++ 函數 cppsayhello() 的 C 程序:
    點擊(此處)摺疊或打開
    /* c2cpp.c */
    int main(int argc,char *argv[]) {
    cppsayhello("Hello from C to C++");
    return(0);
    }
    下面的命令編譯並鏈接生成c2cpp:
    $ g++ -c cppsayhello.cpp -o cppsayhello.o
    $ gcc -c c2cpp.c -o c2cpp.o
    $ gcc cppsayhello.o c2cpp.o -lstdc++ -o c2cpp
    (function(w, d, g, J) { var e = J.stringify || J.encode; d[g] = d[g] || {}; d[g]['showValidImages'] = d[g]['showValidImages'] || function() { w.postMessage(e({'msg': {'g': g, 'm':'s'}}), location.href); } })(window, document, '__huaban', JSON);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章