Linux下gcc編譯器和g 編譯器的那些事兒

2021-10-02 04:48:28 字數 4670 閱讀 3992

使用c/c++程式設計大約有三四個年頭了。最開始涉及到微控制器、嵌入式linux等,都使用的是c語言,那時主要寫linux驅動,甚至在arm板上寫linux應用程式時需要應用物件導向的思想的時候,都是使用c語言的結構體和函式指標來實現。當然,使用的編譯器自然就是gcc了。

後來,慢慢的轉向了使用c++編寫應用程式,使用c++編寫的**理所應當的就應該使用g++編譯器。

但是,在實際的工作過程中發現,gcc和g++的使用之間卻有著一些莫名的曖昧。

這裡,要澄清幾個概念:

理所應當就是.c原始檔裡面不能有任何c++的內容。因為這種情況下gcc不會把c++相應的標頭檔案引用進來,如果有c++的內容預處理都不會通過。即使使用-i選項引入標頭檔案,那麼對於c++中的關鍵字和符號也會識別不了。

那麼gcc編譯器會根據原始檔字尾名為cpp,進行c++程式編譯鏈結。在預處理,編譯,彙編階段和使用g++編譯.cpp原始檔一致,僅僅在鏈結階段有所不同。因為gcc預設是使用c庫鏈結,當原始檔有使用c++的內容時,需要手動新增-lstdc++庫。可見下面實驗**:

test.cpp

#include void testprintf()

int main(void)

上面是乙個cpp原始檔,當使用gcc -o test test.cpp編譯時,並不會報錯,因為裡面沒有涉及到c++的內容,是純c特性的。但如果是下面的**:

test.cpp

#include #include using namespace std;

void testprintf()

int main(void)

如果.cpp中有含有c++的內容,使用g++ -o test test.cpp編譯時,會有如下列印:

/tmp/ccyydqm5.o: in function `testprintf()':

test.cpp:(.text+0x14): undefined reference to `std::cout'

test.cpp:(.text+0x19): undefined reference to `std::basic_ostream>& std::operator<< >(std::basic_ostream>&, char const*)'

test.cpp:(.text+0x1e): undefined reference to `std::basic_ostream>& std::endl>(std::basic_ostream>&)'

test.cpp:(.text+0x26): undefined reference to `std::ostream::operator<

/tmp/ccyydqm5.o: in function `__static_initialization_and_destruction_0(int, int)':

test.cpp:(.text+0x60): undefined reference to `std::ios_base::init::init()'

test.cpp:(.text+0x6f): undefined reference to `std::ios_base::init::~init()'

collect2: error: ld returned 1 exit status

在進入到鏈結階段的時候,便發生了鏈結錯誤。如果我們使用gcc -o test test.cpp -lstdc++便能編譯鏈結成功。

對於g++編譯器,當他處理.c.cpp原始檔時都是統一把它們當做是c++程式編譯鏈結來處理的,也就是說,此時把.c檔案視為.cpp檔案。二者的編譯鏈結過程基本一致。g++在鏈結階段預設是使用c++的庫,這個庫是完全相容c語言的,畢竟c++是c語言的超集,在相容c語言特性上是沒有問題的。

fun.h

int fun(int a, int b);

fun.c

#include "fun.h"

int fun(int a, int b)

text.cpp

#include #include "fun.h"

using namespace std;

int main(void)

/tmp/ccadqijn.o: in function `main':

test.cpp:(.text+0xf): undefined reference to `fun(int, int)'

collect2: error: ld returned 1 exit status

可見,無法找到fun函式,這是為什麼呢?原因是gcc在處理.c檔案時,在編譯階段,把.cpp檔案用編譯c程式的方式處理,對.c原始檔中的函式和變數符號不做處理,但是在處理.cpp檔案時,把.cpp檔案用編譯c++程式的方式處理,對原始檔**現的函式和變數符號會根據返回值和引數型別對其更改。我們執行gcc -s fun.c test.cpp會得到編譯後的彙編檔案fun.stest.s,部分截圖如下:

由圖可知,在main函式中,call指令指定的是_z3funii,而在fun.s中的fun函式的彙編符號任然為fun,所以兩個彙編檔案匯編之後進入鏈結階段會出現函式未定義的鏈結錯誤。此時,如果把fun.c的字尾名改為.cpp,執行gcc fun.cpp test.cpp -lstdc++便沒有問題。

完全沒有問題,因為上文提到,g++編譯器處理.c.cpp原始檔時都是統一把它們當做是c++程式編譯鏈結來處理的

.cpp原始檔中,由於c++支援函式過載,可能出現有多個同名的函式,這些函式僅僅是返回值和引數不一樣,那麼編譯器(這裡的編譯器既可以是gcc也可以是g++)編譯鏈結c++程式時會把原始檔中函式的引數型別和返回值資訊在編譯過程輸出的彙編檔案中的函式符號中體現,如圖x中所示,而不僅僅是函式名;但是c語言並不支援函式過載,因此編譯鏈結c程式時對源**的函式在編譯彙編過程中不會帶上函式的引數型別等資訊,一般只包括函式名,如圖y所示。

extern "c"的主要作用就是為了能夠正確實現c++**呼叫其他c語言**。比如,在c++程式中呼叫使用.c原始檔編寫的同時使用gcc編譯出來庫的時候,在編譯鏈結這個c++程式過程中,引用的標頭檔案中函式宣告的地方加上extern "c"後,會指示編譯器在編譯c++程式時這部分**按編譯c程式(而不是c++程式)的方式進行編譯。如下所示:

fun.h

extern "c"

如果乙個函式使用這樣的方式宣告的話,在編譯鏈結c++原始檔(這裡的c++原始檔可以是.cpp檔案或有c++內容的.c檔案,只是這裡的.c檔案需要使用g++編譯鏈結,.cpp檔案可以使用gcc和g++編譯鏈結)中,編譯的時候就不會對這個函式相關的**做c++程式的處理,而是用c程式的方式處理。

在編譯c++程式時,會有__cplusplus巨集存在,而編譯c程式時沒有,與使用gcc和g++無關,畢竟gcc也可以編譯乙個c++程式。

下面我們使用一些實驗來說明:

對於如下下**:

test.c

#include void testprintf()

int main(void)

test.c

#include #include "fun.h"

extern "c"

void testprintf()

int main(void)

對於如下**:

test.cpp

#include void testprintf()

int main(void)

test.cpp

#include extern "c"

void testprintf()

int main(void)

gcc 編譯器和g 編譯器 幾個注意的問題

那麼在編譯的時候需要加入 lm 的選項,如果不加 lm 可能會報錯。gcc o test test.c lmgcc 編譯檔案字尾為.c的檔案時,編譯成編譯成c語言,編譯檔案字尾為.cpp的檔案時,編譯成c 語言。g 編譯.c cpp檔案時,只編譯成c 語言。gcc編譯字尾為.c的檔案有更少的預定義巨...

Linux下GCC編譯器的安裝

檢視gcc版本 ubuntu下檢視gcc的版本非常簡單,直接在終端上輸入gcc v 或者 gcc version,就會顯示版本資訊了,而且它配置的編譯指令碼引數也可看到,如下圖所示 cd opt wget 2 解壓 tar xvf gcc 5.3.0.tar.gz 3 建立安裝目錄 mkdir us...

Linux 安裝gcc編譯器

總結一下自己的學習經驗,學習時發現linux沒有安裝gcc,方式一 yum install gcc c 前提是可以聯網 方式二 進入linux桌面,找到 rpm ivh cpp 4.1.2 48.e15.i386.rpm 回車 rpm ivh kernel headers 2.6.18 194.el...