程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> c/c++語言基礎——編譯和鏈接

c/c++語言基礎——編譯和鏈接

編輯:關於C++

用慣了圖形化的IDE,對編譯和鏈接部分的東西知道的病不多,這裡補習一下。

1. 首先編譯和鏈接是兩個東西,雖然在IDE上按一個鍵結果就出來了,這是因為IDE在後台自己做了不少的事情。

2. 常見的編譯器有gcc/g++,前者是給c用的,後者是給c++用的,兩者的編譯操作差別不大;常見的連接器有ld。

3. gcc會把鏈接的事情也做掉(除非加參數),所以對ld的了解更少...

下面以gcc和ld為例說明c語言(c++也差不多,只是要換編譯器為g++)的編譯和鏈接。

首先看下面的一個最簡單的例子:

 

#include 

int main(int argc, const char * argv[]) 
{
    printf("Hello, World!\n");
    return 0;
}
在IDE下,比如這邊使用的Xcode,只要Cmd+R,就可以直接得到運行結果了:

 

\
雖然不明白IDE打的做了什麼,但是覺得很厲害的樣子。

對大部分只關注代碼實現的人來說,能夠正確使用就OK了,但是如果希望進一步了解代碼的編譯和鏈接,則需要關掉IDE,打開shell,來探索gcc跟ld了。

 

gcc的基本格式如下:

 

gcc [option] files

先不管參數部分,下面是對上面的main.c代碼運行gcc的結果:

 

\

可以看到也能得到與Xcode執行時一樣的結果。

連ld都省了。

到底gcc做了些什麼,可以參考Journey of a C Program to Linux Executable in 4 Stages這篇文章。

 

接下來需要介紹一些常用的gcc選項,上面的例子中,雖然我們沒有使用編譯選項,但是gcc還是會根據默認的選項值來進行編譯。

當我們了解了gcc的編譯選項,就可以更好的控制編譯的過程。

下面就是一些常用的選項:

-o file:給輸出文件命名,在上例中,我們沒有指定名稱,所以使用了默認的a.out,如果你指定了名稱,比如下面這樣,就會有不同的結果:

\

-Wall:這個是用來顯示編譯時的告警的。比如在我們的代碼中添加一個聲明了但未使用的變量:

\

如果不加-Wall參數,就不會報錯。

開啟-Wall可以讓上報一些我們自己沒有發現的問題,值得推薦。

-Werror:這個選項的意思是把編譯中的warning當作錯誤。上面的例子中,雖然-Wall之後編譯時會有提示,但是最終還是會生成a.out,即整個編譯過程是會運行完成的,而如果又加了-Werror,在編譯過程中就報錯並終止了:

\

-Wall -Werror一起使用,能夠不放過任何的編譯問題。

-S:用來生成對應的匯編代碼:

\

查看匯編代碼:

\

可以看到生成的匯編代碼是AT&T格式的,但是如果只知道Intel的匯編格式呢?可以再加上-masm=intel這個參數:

\

這樣生成的就是intel格式的匯編代碼了:

\

現在需要用到匯編的機會不多,除非是性能要求需要優化代碼,或者debug的時候才會去用吧。

-E:加入了-E選項後,gcc並不進行編譯,而是進行預編譯,就是把宏進行替換、頭文件展開等。在原來的代碼上加一個宏,下面是執行-E後的情況:

\

-c:指定這個選項後,gcc只負責編譯,但是不會進行鏈接:

\

生成的.o文件需要經過鏈接才能成為可執行的文件,這裡就需要用到ld了,先不介紹。

-save-temps:使用這個命令可以保留在gcc編譯和鏈接過程中的中間文件:

\

這些中間文件對於了解gcc的工作過程很有幫助。很多文件在之前已經介紹過了,另外的,main.i就是預處理後的代碼,main.bc是什麼還不清楚...

-l和-L:這兩個是用來指定庫的,前者指定庫名,後者指定庫的路徑,可以參考http://blog.csdn.net/jiangwei0512/article/details/51559030的說明。

-v:加上這個參數可以在編譯時看到不少額外的信息:

\

-D:後接宏來控制代碼的走向。

#include 
#define MAX 255
int main(int argc, const char * argv[]) 
{
#ifdef ERROR
	aaaa;
#endif
	int i = MAX;
	printf("Hello, World!\n");
	return 0;
}
上面的代碼中,ERROR宏控制的代碼是錯的,如果使用-DERROR,則在編譯的時候就會包含aaaa這樣的代碼,導致編譯錯誤:

 

\

而不加-D,就不會報錯。

這裡需要注意下-D和宏之間沒有空格。

編譯參數介紹到這裡,下面需要介紹鏈接器ld。

 

ld和gcc使用的方式差不多,也是ld +選項 +文件。

這裡的文件需要時.o文件,即gcc -c編譯後的文件。

ld最重要的作用當然是生成可執行的文件。但是還有一個非常重要的作用,就是找到我們的代碼中並沒有定義的那些函數,然後在當前平台上找到相應的實現。

比如我們的代碼中有一個printf,它是c標准庫中實現的,所以鏈接時就需要找到相應的庫,並獲取到printf的實現代碼。

下面是鏈接的操作過程:

首先,當然要生成.o文件,參見前面講-c時的圖片;

然後,像使用gcc一樣,什麼參數也不加來執行下:

\

發現有錯誤,並沒有生成可執行文件。

前兩個warning是針對mac的,通過加參數-macosx_version_min就可以解決。

後一個錯誤是因為沒有找到_printf的實現,這是因為我們沒有指定庫。

下面是修改後的執行命令:

\

可以看到能夠生成a.out了,但是還是有報之前的warning,原因是因為沒有指定系統架構,修改成下面的形式:

\

就可以得到我們需要的結果了。

 

下面也介紹下ld常用的選項(像-macox_verson_min這種就不說了):

-arch:用來指定系統架構,比如Intel 32位機是i386,Intel 64位機是x86_64。

-lx:對應上面的命令就是-lc,表示的是尋找libc.a或者libc.dylib這樣的庫,因為printf就在這些庫中實現的。

-e:用來指定入口,默認是_main,對應到代碼中就是main,所以我們不需要顯式得指定,當然指定也沒有關系:

\

 

以上就是對gcc和ld的介紹,它們可以帶的選項還有很多,要了解具體所有選線,可以在shell下使用man gcc/ld來查看。

PS:發現一個問題,在mac中gcc被指向了clang編譯器,所以上面的gcc操作實際上都是clang的操作......不過基本的功能都沒有變,參數在gcc中也能夠正常使用,所以基本上也算是gcc的說明吧......

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved