C語言源程序至可執(zhí)行文件步驟_第1頁
C語言源程序至可執(zhí)行文件步驟_第2頁
C語言源程序至可執(zhí)行文件步驟_第3頁
C語言源程序至可執(zhí)行文件步驟_第4頁
C語言源程序至可執(zhí)行文件步驟_第5頁
已閱讀5頁,還剩10頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、C/C+福序編譯步驟如何生成可執(zhí)行文件電子計(jì)算機(jī)所使用的是由“ 0和”“ 1組成的二進(jìn)制數(shù),二進(jìn)制是計(jì)算機(jī)的語言”的基礎(chǔ)。計(jì)算機(jī)發(fā)明之初,人們只能降貴紆尊,用計(jì)算機(jī)的語言去命令計(jì)算機(jī)干這干那,一句話,就是寫出一串串由“ 0和”“ 1組成的指令序列交由計(jì)算機(jī)執(zhí)”行,這種語言,就是機(jī)器語言。想象一下老前輩們?cè)诖蚩讬C(jī)面前數(shù)著一個(gè)孔的情景,噓,小聲點(diǎn),你的驚嚇可能使他們錯(cuò)過了一個(gè)孔,結(jié)果可能是導(dǎo)致一艘飛船飛離軌道阿。為了減輕使用機(jī)器語言編程的痛苦,人們進(jìn)行了一種有益的改進(jìn):用一些簡潔的英文字母、符號(hào)串來替代一個(gè)特定的指令的二進(jìn)制串,比如,用“A D求表加法,“MO V弋表數(shù)據(jù)傳遞等等,這樣一來,人們很

2、容易讀懂并理解程序在干什么,糾錯(cuò)及維護(hù)都變得方便了,這種程序設(shè)計(jì)語言就稱為匯編語言,即第二代計(jì)算機(jī)語言。然而計(jì)算機(jī)是不認(rèn)識(shí)這些符號(hào)的,這就需要一個(gè)專門的程序,專門負(fù)責(zé)將這些符號(hào)翻譯成二進(jìn)制數(shù)的機(jī)器語言,這種翻譯程序被稱為匯編程序。因?yàn)閰R編指令和機(jī)器語言之間有著一一對(duì)應(yīng)的關(guān)系,這可比英譯漢或漢譯英簡單多了。高級(jí)語言是偏向人,按照人的思維方式設(shè)計(jì)的,機(jī)器對(duì)這些可是莫名奇妙,不知所謂。魚與熊掌的故事在計(jì)算機(jī)語言中發(fā)生了。于是必須要有一個(gè)橋梁來銜接兩者,造橋可不是一件簡單的事情。當(dāng)你越想方便,那橋就得越復(fù)雜。那高級(jí)語言是如何變成機(jī)器語言的呢,這個(gè)過程讓我慢慢道來。編譯:將源代碼轉(zhuǎn)換為機(jī)器可認(rèn)識(shí)代碼的過

3、程。編譯程序讀取源程序(字符流),對(duì)之進(jìn)行詞法和語法的分析,將高級(jí)語言指令轉(zhuǎn)換為功能等效的匯編代碼,再由匯編程序轉(zhuǎn)換為機(jī)器語言,并且按照操作系統(tǒng)對(duì)可執(zhí)行文件格式的要求鏈接生成可執(zhí)行程序。C源程序- 編譯預(yù)處理- 編譯- 優(yōu)化程序- 匯編程序 鏈接程序可執(zhí)行文件. 編譯預(yù)處理讀取c 源程序,對(duì)其中的偽指令(以#開頭的指令)和特殊符號(hào)進(jìn)行處理。偽指令主要包括以下四個(gè)方面:(1)宏定義指令,如# define Name TokenString,#undef等。對(duì)于前一個(gè)偽 指令,預(yù)編譯所要作得的是將程序中的所有 Name用TokenString替換,但作為 字符串常量的 Name 則不被替換。對(duì)于后

4、者,則將取消對(duì)某個(gè)宏的定義,使以后該串的出現(xiàn)不再被替換。(2)條件編譯指令,如#ifdef,#ifndef,#else,#elif,#endif,等等。這些偽指令的 引入使得程序員可以通過定義不同的宏來決定編譯程序?qū)δ男┐a進(jìn)行處理。預(yù)編譯程序?qū)⒏鶕?jù)有關(guān)的文件,將那些不必要的代碼過濾掉。(3)頭文件包含指令,如 #include FileName或者#include FileName冷。 在頭文件中一般用偽指令#define 定義了大量的宏(最常見的是字符常量),同時(shí)包含有各種外部符號(hào)的聲明。采用頭文件的目的主要是為了使某些定義可以供多個(gè)不同的C源程序使用。因?yàn)樵谛枰玫竭@些定義的C源程序中,

5、只需加上一條#include語句即可,而不必再在此文件中將這些定義重復(fù)一遍。預(yù)編譯程 序?qū)杨^文件中的定義統(tǒng)統(tǒng)都加入到它所產(chǎn)生的輸出文件中,以供編譯程序?qū)χM(jìn)行處理。包含到 c 源程序中的頭文件可以是系統(tǒng)提供的,這些頭文件一般被放在 /usr/include 目錄下。在程序中 #include 它們要使用尖括號(hào)( )。另外 開發(fā)人員也可以定義自己的頭文件,這些文件一般與c源程序放在同一目錄下,此時(shí)在#include 中要用雙引號(hào)( )。( 4)特殊符號(hào),預(yù)編譯程序可以識(shí)別一些特殊的符號(hào)。例如在源程序中出現(xiàn)的LINE標(biāo)識(shí)將被解釋為當(dāng)前行號(hào)(十進(jìn)制數(shù)),F(xiàn)ILE則被解釋為當(dāng)前被編譯 的C源程序的名

6、稱。預(yù)編譯程序?qū)τ谠谠闯绦蛑谐霈F(xiàn)的這些串將用合適的值進(jìn) 行替換。預(yù)編譯程序所完成的基本上是對(duì)源程序的 “替代 ”工作。經(jīng)過此種替代,生成一個(gè)沒有宏定義、沒有條件編譯指令、沒有特殊符號(hào)的輸出文件。這個(gè)文件的含義同沒有經(jīng)過預(yù)處理的源文件是相同的,但內(nèi)容有所不同。下一步,此輸 出文件將作為編譯程序的輸出而被翻譯成為機(jī)器指令。.編譯階段經(jīng)過預(yù)編譯得到的輸出文件中,將只有常量。如數(shù)字、字符串、變量的定義,以及C語言的關(guān)鍵字,如,等等。預(yù)編譯程序所要作得工作就是通過詞法分析和語法分析,在確認(rèn)所有的指令都符合語法規(guī)則之后,將其翻譯成等價(jià)的中間代碼表示或匯編代碼。.優(yōu)化階段優(yōu)化處理是編譯系統(tǒng)中一項(xiàng)比較艱深的技

7、術(shù)。它涉及到的問題不僅同編譯技術(shù)本身有關(guān),而且同機(jī)器的硬件環(huán)境也有很大的關(guān)系。優(yōu)化一部分是對(duì)中間代碼的優(yōu)化。這種優(yōu)化不依賴于具體的計(jì)算機(jī)。另一種優(yōu)化則主要針對(duì)目標(biāo)代碼的生成而進(jìn)行的。上圖中,我們將優(yōu)化階段放在編譯程序的后面,這是一種比較籠統(tǒng)的表示。對(duì)于前一種優(yōu)化,主要的工作是刪除公共表達(dá)式、循環(huán)優(yōu)化(代碼外提、強(qiáng)度削弱、變換循環(huán)控制條件、已知量的合并等)、復(fù)寫傳播,以及無用賦值的刪除,等等。后一種類型的優(yōu)化同機(jī)器的硬件結(jié)構(gòu)密切相關(guān),最主要的是考慮是如何充分利用機(jī)器的各個(gè)硬件寄存器存放的有關(guān)變量的值,以減少對(duì)于內(nèi)存的訪問次數(shù)。另外,如何根據(jù)機(jī)器硬件執(zhí)行指令的特點(diǎn)(如流水線、 RISC、 CISC

8、、VLIW等)而對(duì)指令進(jìn)行一些調(diào)整使目標(biāo)代碼比較短,執(zhí)行的效率比較 高,也是一個(gè)重要的研究課題。經(jīng)過優(yōu)化得到的匯編代碼必須經(jīng)過匯編程序的 匯編轉(zhuǎn)換成相應(yīng)的機(jī)器指令,方可能被機(jī)器執(zhí)行。4.匯編過程匯編過程實(shí)際上指把匯編語言代碼翻譯成目標(biāo)機(jī)器指令的過程。對(duì)于被翻譯系統(tǒng)處理的每一個(gè)C語言源程序,都將最終經(jīng)過這一處理而得到相應(yīng)的目標(biāo) 文件。目標(biāo)文件中所存放的也就是與源程序等效的目標(biāo)的機(jī)器語言代碼。目標(biāo)文件由段組成。通常一個(gè)目標(biāo)文件中至少有兩個(gè)段:代碼段該段中所包含的主要是程序的指令。該段一般是可讀和可執(zhí)行的,但一般卻不可寫。數(shù)據(jù)段主要存放程序中要用到的各種全局變量或靜態(tài)的數(shù)據(jù)。一般數(shù)據(jù)段都是可讀,可寫

9、,可執(zhí)行的。UNIX環(huán)境下主要有三種類型的目標(biāo)文件:1)可重定位文件其中包含有適合于其它目標(biāo)文件鏈接來創(chuàng)建一個(gè)可執(zhí)行的或者共享的目標(biāo)文件的代碼和數(shù)據(jù)。2)共享的目標(biāo)文件這種文件存放了適合于在兩種上下文里鏈接的代碼和數(shù)據(jù)。第一種事鏈接程序可把它與其它可重定位文件及共享的目標(biāo)文件一起處理來創(chuàng)建另一個(gè)目標(biāo)文件;第二種是動(dòng)態(tài)鏈接程序?qū)⑺c另一個(gè)可執(zhí)行文件及其它的共享目標(biāo)文件結(jié)合到一起,創(chuàng)建一個(gè)進(jìn)程映象。3)可執(zhí)行文件它包含了一個(gè)可以被操作系統(tǒng)創(chuàng)建一個(gè)進(jìn)程來執(zhí)行之的文件。匯編程序生成的實(shí)際上是第一種類型的目標(biāo)文件。對(duì)于后兩種還需要其他的一些處理方能得到,這個(gè)就是鏈接程序的工作了。5.鏈接程序由匯編程序生

10、成的目標(biāo)文件并不能立即就被執(zhí)行,其中可能還有許多沒有解決的問題。例如,某個(gè)源文件中的函數(shù)可能引用了另一個(gè)源文件中定義的某個(gè)符號(hào)(如變量或者函數(shù)調(diào)用等);在程序中可能調(diào)用了某個(gè)庫文件中的函數(shù),等等。所有的這些問題,都需要經(jīng)鏈接程序的處理方能得以解決。鏈接程序的主要工作就是將有關(guān)的目標(biāo)文件彼此相連接,也即將在一個(gè)文件中引用的符號(hào)同該符號(hào)在另外一個(gè)文件中的定義連接起來,使得所有的這些目標(biāo)文件成為一個(gè)能夠被操作系統(tǒng)裝入執(zhí)行的統(tǒng)一整體。根據(jù)開發(fā)人員指定的同庫函數(shù)的鏈接方式的不同,鏈接處理可分為兩種:1)靜態(tài)鏈接在這種鏈接方式下,函數(shù)的代碼將從其所在地靜態(tài)鏈接庫中被拷貝到最終的可執(zhí)行程序中。這樣該程序在被

11、執(zhí)行時(shí)這些代碼將被裝入到該進(jìn)程的虛擬地址空間中。靜態(tài)鏈接庫實(shí)際上是一個(gè)目標(biāo)文件的集合,其中的每個(gè)文件含有庫中的一個(gè)或者一組相關(guān)函數(shù)的代碼。2)動(dòng)態(tài)鏈接在此種方式下,函數(shù)的代碼被放到稱作是動(dòng)態(tài)鏈接庫或共享對(duì)象的某個(gè)目標(biāo)文件中。鏈接程序此時(shí)所作的只是在最終的可執(zhí)行程序中記錄下共享對(duì)象的名字以及其它少量的登記信息。在此可執(zhí)行文件被執(zhí)行時(shí),動(dòng)態(tài)鏈接庫的全部內(nèi)容將被映射到運(yùn)行時(shí)相應(yīng)進(jìn)程的虛地址空間。動(dòng)態(tài)鏈接程序?qū)⒏鶕?jù)可執(zhí)行程序中記錄的信息找到相應(yīng)的函數(shù)代碼。對(duì)于可執(zhí)行文件中的函數(shù)調(diào)用,可分別采用動(dòng)態(tài)鏈接或靜態(tài)鏈接的方法。使用動(dòng)態(tài)鏈接能夠使最終的可執(zhí)行文件比較短小,并且當(dāng)共享對(duì)象被多個(gè)進(jìn)程使用時(shí)能節(jié)約一些

12、內(nèi)存,因?yàn)樵趦?nèi)存中只需要保存一份此共享對(duì)象的代碼。但并不是使用動(dòng)態(tài)鏈接就一定比使用靜態(tài)鏈接要優(yōu)越。在某些情況下動(dòng)態(tài)鏈接可能帶來一些性能上損害。經(jīng)過上述五個(gè)過程,C源程序就最終被轉(zhuǎn)換成可執(zhí)行文件了上一節(jié)我們介紹了編程語言的種類,其中包括機(jī)器語言、匯編語言和高級(jí)語言。C/C+理序編譯步驟詳解C/C+錯(cuò)言很多人都比較熟悉,這基本上是每位大學(xué)生必學(xué)的一門編程語言,通常還都是作為程序設(shè)計(jì)入門語言學(xué)的,并且課程大多安排在大一。剛上大學(xué),孩子們還都很乖,學(xué)習(xí)也比較認(rèn)真,用心。所以,C/C+畸言掌握地也都不錯(cuò),不用說編譯程序,就是寫個(gè)上幾百行的程序都不在話下,但是他們真的知道C/C+序編譯的步驟么?我想很多人

13、都不甚清楚,如果他接下來學(xué)過 “編譯原理 ” ,也許能說個(gè)大概。VC的舒適”開發(fā)環(huán)境屏蔽了很多編譯的細(xì)節(jié),這無疑降低了初學(xué)者的入門門檻,但是也 “剝奪 ”了他們 “知其所以然”的權(quán)利,致使很多東西只能死記硬背,遇到相關(guān)問題就 “丈二 ” 。實(shí)際上,我也是在學(xué)習(xí) Linux 環(huán)境下編程的過程中才逐漸弄清楚C/C+代碼是如何一步步變成可執(zhí)行文件的。總體來說,C/C+堿代碼要經(jīng)過:預(yù)處理、編譯、匯編和鏈接四步才能變成相應(yīng)平臺(tái)下的可執(zhí)行文件。大多數(shù)時(shí)候,程序員通過一個(gè)命令就能完成上述四個(gè)步驟。比如下面這段C的“Helloworld ! ”代碼 :File:hw.c#include stdio.hint

14、 main(intargc, char *argv)printf(Hello World!n);return 0;如果用gcc編譯,只需要一個(gè)命令就可以生成可執(zhí)行文件hw:xiaosuogentuxhw $ gcc -o hw.cxiaosuogentuxhw $ ./hw Hello World!我們可以用 -v 參數(shù)來看看gcc 到底在背后都做了些什么動(dòng)作 :Reading specs from /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/specsConfigured with:/var/tmp/portage/sys-devel/gcc-3.4.6-r2/

15、work/gcc-3.4.6/configure -prefix=/usr -bindir=/usr/i686-pc-linux-gnu/gcc-bin/3.4.6 -includedir=/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/include -datadir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.6 -mandir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.6/man -infodir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.

16、6/info -with-gxx-include-dir=/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/include/g+-v3 -host=i686-pc-linux-gnu -build=i686-pc-linux-gnu -disable-altivec -enable-nls -without-included-gettext -with-system-zlib -disable-checking -disable-werror -enable-secureplt -disable-libunwind- exceptions -disable-multil

17、ib -disable-libgcj -enable-languages=c,c+,f77 - -enable-shared -enable-threads=posix -enable-_cxa_atexit -enable- clocale=gnuThread model:posixgcc version3.4.6 (Gentoo3.4.6-r2, ssp-3.4.61.0, pie-8.7.10)/usr/libexec/gcc/i686-pc-linux-gnu/3.4.6/cc1 -quiet -v hw.c -quiet -dumpbasehw.c -mtune=pentiumpro

18、 -auxbasehw -version -o /tmp/ccYB6UwR.signoring nonexistent directory /usr/local/includeignoring nonexistent directory /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/././././i686-pc-linux-gnu/include#include . search starts here:#include . search starts here:/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/include/usr/in

19、cludeEnd of search list.GNU C version3.4.6 (Gentoo3.4.6-r2, ssp-3.4.61.0, pie-8.7.10) (i686-pc-linux-gnu)3.4.6 (Gentoo3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.9).GGC heuristics:-paramggc-min-expand=81 -paramggc-min-heapsize=97004/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/././././i686-pc-linux-gnu/bin/as -V-Qy -o

20、/tmp/ccq8uGED.o /tmp/ccYB6UwR.sGNU assembler version2.17 (i686-pc-linux-gnu) using BFD version2.17/usr/libexec/gcc/i686-pc-linux-gnu/3.4.6/collect2 -eh-frame-hdr -m elf_i386-dynamic-linker /lib/ld-linux.so.2 -o hw /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/./././crt1.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6

21、/./././crti.o/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtbegin.o -L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6 -L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6 -L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/././././i686-pc-linux-gnu/lib -L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/././. /tmp/ccq8uGED.o -lgcc -as-need -lgcc_s -no-as-n

22、eed -lc -lgcc -as-need -lgcc_s -no-as-need /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtend.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/./././crtn.o稍微整理一下,去掉一些冗余信息后,如下 :cc1 hw.c -o /tmp/ccYB6UwR.sas -o /tmp/ccq8uGED.o /tmp/ccYB6UwR.sld -o hw /tmp/ccq8uGED.o以上三個(gè)命令分別對(duì)應(yīng)于編譯步驟中的預(yù)處理+編譯、匯編和連接。預(yù)處理和編譯還是放在了一個(gè)命令(cc1)中進(jìn)行

23、的,可以把它再次拆分為以下兩步:cpp -o hw.ihw.ccc1 hw.i -o /tmp/ccYB6UwR.s一個(gè)精簡過的能編譯以上 hw.c文件的Makefile如下:.PHONY:cleanall:hwhw:hw.old -dynamic-linker /lib/ld-linux.so.2 -o hw /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtbegin.o hw.o -lc /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtend.o /usr/lib

24、/crtn.ohw.o:hw.sas -o hw.ohw.shw.s:hw.i/usr/libexec/gcc/i686-pc-linux-gnu/3.4.6/cc1 -o hw.shw.chw.i:hw.ccpp -o hw.ihw.cclean:rm -rfhw.ihw.shw.o當(dāng)然,上面Makefile 中的一些路徑是我系統(tǒng)上的具體情況,你的可能與我的不同。接下來我們按照編譯順序看看編譯器每一步都做了什么。首先是預(yù)處理,預(yù)處理后的文件 hw.i:1 hw.c1 1 ._extension_ typedef _quad_t _off64_t;_extension_ typedefint

25、_pid_t;_extension_ typedefstruct int _val2; _fsid_t;.extern int remove (_const char *_filename) _attribute_ (_nothrow_); extern int rename (_const char *_old, _const char *_new) _attribute_ (_nothrow_);int main(intargc, char *argv)printf(Hello World!n);return 0; 注:由于文件比較大,所以只留下了少部分具有代表性的內(nèi)容??梢钥匆婎A(yù)處理器把

26、所有要包含(include)的文件(包括遞歸包含的文 件)的內(nèi)容都添加到了原始的C源文件中,然后把其輸出到輸出文件,除此之外,它還展開了所有的宏定義,所以在預(yù)處理器的輸出文件中你將找不到任何宏。這也提供了一個(gè)查看宏展開結(jié)果的簡便方法。第二步 編譯,就是把C/C+弋碼 翻譯”成匯編代碼:.file hw.c.section .rodata.LC0:.string Hello World!n.text.globl main.type main, functionmain:pushl?pmovl %esp,?psubl $8, %espandl $-16, %espmovl $0,?xaddl $1

27、5,?xaddl $15,?xshrl $4,?xsall $4,?xsubl?x, %espsubl $12, %esppushl $.LC0call printfaddl $16, %espmovl $0,?xleaveret.size main, .-main.section .note.GNU-stack,progbits.ident GCC:(GNU)3.4.6 (Gentoo3.4.6-r2, ssp-3.4.61.0, pie-8.7.10)這個(gè)匯編文件比預(yù)處理后的C/C+位件小了很多,去除了很多不必要的東西,比如說沒用到的類型聲明和函數(shù)聲明等。第三步 “匯編 ” ,將第二步輸出的匯編代碼翻譯成符合一定格式的機(jī)器代 碼,在Linux上一般表現(xiàn)為ELF目標(biāo)文件。xiaosuogent

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論