




下載本文檔
版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、要想讀懂本文,你需要對(duì) C語(yǔ)言有基本的了解,本文將介紹如何使用gcc編譯器。首先,我們介紹如何在命令行方式下使用編譯器編譯簡(jiǎn)單的C源代碼。然后,我們簡(jiǎn)要介紹一下編譯器究竟作了那些工作,以及如何控制編譯過(guò) 程。我們也簡(jiǎn)要介紹了調(diào)試器的使用方法。GCC rules你能想象使用封閉源代碼的私有編譯器編譯自由軟件嗎?你怎么知道編譯 器在你的可執(zhí)行文件中加入了什么?可能會(huì)加入各種后門(mén)和木馬。Ken Thompson是一個(gè)著名的黑客,他編寫(xiě)了一個(gè)編譯器,當(dāng)編譯器編譯自己 時(shí),就在'login'程序中留下后門(mén)和永久的木馬。請(qǐng)到這里閱讀他對(duì)這個(gè)杰作的描 述。幸運(yùn)的是,我們有了 gcco 當(dāng)你進(jìn)
2、行 con figure;make;make in stall時(shí),gcc在 幕后做了很多繁重的工作。如何才能讓gcc為我們工作呢?我們將開(kāi)始編寫(xiě)一個(gè)紙牌游戲,不過(guò)我們只是為了演示編譯器的功能,所以盡可能地精簡(jiǎn)了代 碼。我們將從頭開(kāi)始一步地做,以便理解編譯過(guò)程,了解為了制作可執(zhí)行文件 需要做些什么,按什么順序做。我們將看看如何編譯C程序,以及如何使用編譯選項(xiàng)讓gcc按照我們的要求工作。步驟(以及所用工具)如下:預(yù)編譯(gcc -E)編譯(gcc),匯編(as),和連接(Id)。開(kāi)始 .首先,我們應(yīng)該知道如何調(diào)用編譯器。實(shí)際上,這很簡(jiǎn)單。我們將從那個(gè) 著名的第一個(gè)C程序開(kāi)始。(各位老前輩,請(qǐng)?jiān)徫?/p>
3、)。#include <stdio.h>int main()printf("Hello World!n");把這個(gè)文件保存為 game.c。你可以在命令 行下編譯它:gcc game.c在默認(rèn)情況下,C編譯器將生成一個(gè)名為a.out的可執(zhí)行文件。你可以鍵入 如下命令運(yùn)行它:a.outHello World每一次編譯程序時(shí),新的 a.out 將覆蓋原來(lái)的程序。你無(wú)法知道是哪個(gè)程序創(chuàng)建了 a.out。我們可以通過(guò)使用-o編譯選項(xiàng),告訴gcc我們想把可執(zhí)行文件叫 什么名字。我們將把這個(gè)程序叫做 game,我們可以使用任何名字,因?yàn)?C沒(méi)有 Java那樣的命名限制。gc
4、c -o game game.cgameHello World到現(xiàn)在為止,我們離一個(gè)有用的程序還差得很遠(yuǎn)。如果你覺(jué)得沮喪,你可 以想一想我們已經(jīng)編譯并運(yùn)行了一個(gè)程序。因?yàn)槲覀儗⒁稽c(diǎn)為這個(gè)程序添加功 能,所以我們必須保證讓它能夠運(yùn)行。似乎每個(gè)剛開(kāi)始學(xué)編程的程序員都想一 下子編一個(gè) 1000 行的程序,然后一次修改所有的錯(cuò)誤。沒(méi)有人,我是說(shuō)沒(méi)有 人,能做到這個(gè)。你應(yīng)該先編一個(gè)可以運(yùn)行的小程序,修改它,然后再次讓它 運(yùn)行。這可以限制你一次修改的錯(cuò)誤數(shù)量。另外,你知道剛才做了哪些修改使 程序無(wú)法運(yùn)行,因此你知道應(yīng)該把注意力放在哪里。這可以防止這樣的情況出 現(xiàn):你認(rèn)為你編寫(xiě)的東西應(yīng)該能夠工作,它也能通過(guò)
5、編譯,但它就是不能運(yùn) 行。請(qǐng)切記,能夠通過(guò)編譯的程序并不意味著它是正確的。下一步為我們的游戲編寫(xiě)一個(gè)頭文件。頭文件把數(shù)據(jù)類(lèi)型和函數(shù)聲明集中 到了一處。這可以保證數(shù)據(jù)結(jié)構(gòu)定義的一致性,以便程序的每一部分都能以同 樣的方式看待一切事情。#ifndef DECK_H#define DECK_H#define DECKSIZE 52typedef struct deck_tint cardDECKSIZE;/* number of cards used */int dealt;deck_t;#endif /* DECK_H */把這個(gè)文件保存為deck.h。只能編譯.c文件,所以我們必須修改game.
6、c。 在game.c的第2行,寫(xiě)上#include"deck.h"。在第5行寫(xiě)上deck_tdeck;。為了保 證我們沒(méi)有搞錯(cuò),把它重新編譯一次。gcc -o game game.c如果沒(méi)有錯(cuò)誤,就沒(méi)有問(wèn)題。如果編譯不能通過(guò),那么就修改它直到能通 過(guò)為止。預(yù)編譯編譯器是怎么知道deck_t類(lèi)型是什么的呢?因?yàn)樵陬A(yù)編譯期間,它實(shí)際上 把"deck.h"文件復(fù)制到了 "game.c"文件中。源代碼中的預(yù)編譯指示以"#"為前綴。 你可以通過(guò)在gcc后加上-E選項(xiàng)來(lái)調(diào)用預(yù)編譯器。幾乎有 3200 行的輸出!其中大多數(shù)來(lái)自
7、stdio.h 包含文件,但是如果你查 看這個(gè)文件的話(huà),我們的聲明也在那里。如果你不用-o選項(xiàng)指定輸出文件名的話(huà),它就輸出到控制臺(tái)。預(yù)編譯過(guò)程通過(guò)完成三個(gè)主要任務(wù)給了代碼很大的靈 活性。把"in clude"的文件拷貝到要編譯的源文件中。用實(shí)際值替代"define"的文本。在調(diào)用XX的地方進(jìn)行XX替換。這就使你能夠在整個(gè)源文件中使用符號(hào)常量(即用DECKSIZ表示一付牌中的紙牌數(shù)量),而符號(hào)常量是在一個(gè)地方定義的,如果它的值發(fā)生了變化,所 有使用符號(hào)常量的地方都能自動(dòng)更新。在實(shí)踐中,你幾乎不需要單獨(dú)使用-E選項(xiàng),而是讓它把輸出傳送給編譯器。編譯作為一個(gè)中
8、間步驟, gcc 把你的代碼翻譯成匯編語(yǔ)言。它一定要這樣做,它 必須通過(guò)分析你的代碼搞清楚你究竟想要做什么。如果你犯了語(yǔ)法錯(cuò)誤,它就 會(huì)告訴你,這樣編譯就失敗了。人們有時(shí)會(huì)把這一步誤解為整個(gè)過(guò)程。但是,實(shí)際上還有許多工作要 gcc 去做呢。匯編as把匯編語(yǔ)言代碼轉(zhuǎn)換為目標(biāo)代碼。事實(shí)上目標(biāo)代碼并不能在 CPU上運(yùn) 行,但它離完成已經(jīng)很近了。編譯器選項(xiàng)-c把.C文件轉(zhuǎn)換為以.0為擴(kuò)展名的目 標(biāo)文件。如果我們運(yùn)行 gcc -c game.c我們就自動(dòng)創(chuàng)建了一個(gè)名為game.o的文件。這里我們碰到了一個(gè)重要的問(wèn) 題。我們可以用任意一個(gè).c文件創(chuàng)建一個(gè)目標(biāo)文件。正如我們?cè)谙旅嫠吹?的,在連接步驟中我們
9、可以把這些目標(biāo)文件組合成可執(zhí)行文件。讓我們繼續(xù)介 紹我們的例子。因?yàn)槲覀冋诰帉?xiě)一個(gè)紙牌游戲,我們已經(jīng)把一付牌定義為 deck_t,我們將編寫(xiě)一個(gè)洗牌函數(shù)。這個(gè)函數(shù)接受一個(gè)指向deck類(lèi)型的指針,并把一付隨機(jī)的牌裝入deck類(lèi)型。它使用'drawn'數(shù)組跟蹤記錄那些牌已經(jīng)用過(guò) 了。這個(gè)具有DECKSIZ個(gè)元素的數(shù)組可以防止我們重復(fù)使用一張牌。#include <stdlib.h>#include <stdio.h>#include <time.h>#include "deck.h"static time_t seed =
10、0;void shuffle(deck_t *pdeck)/* Keeps track of what numbers have been used */ int drawnDECKSIZE = 0;int i;/* One time initialization of rand */if(0 = seed)seed = time(NULL);srand(seed);for(i = 0; i < DECKSIZE; i+)int value = -1;dovalue = rand() % DECKSIZE;while(drawnvalue != 0);/* mark value as u
11、sed */drawnvalue = 1;/* debug statement */printf("%in", value);pdeck->cardi = value;pdeck->dealt = 0;return;把這個(gè)文件保存為shuffle.c。我們?cè)谶@個(gè)代碼中加入了一條調(diào)試語(yǔ) 句,以便運(yùn)行時(shí),能輸出所產(chǎn)生的牌號(hào)。這并沒(méi)有為我們的程序添加功能,但 是現(xiàn)在到了關(guān)鍵時(shí)刻,我們看看究竟發(fā)生了什么。因?yàn)槲覀兊挠螒蜻€在初級(jí)階 段,我們沒(méi)有別的辦法確定我們的函數(shù)是否實(shí)現(xiàn)了我們要求的功能。使用那條 printf 語(yǔ)句,我們就能準(zhǔn)確地知道現(xiàn)在究竟發(fā)生了什么,以便在開(kāi)始下一
12、階段之 前我們知道牌已經(jīng)洗好了。在我們對(duì)它的工作感到滿(mǎn)意之后,我們可以把那一 行語(yǔ)句從代碼中刪掉。這種調(diào)試程序的技術(shù)看起來(lái)很粗糙,但它使用最少的語(yǔ) 句完成了調(diào)試任務(wù)。以后我們?cè)俳榻B更復(fù)雜的調(diào)試器。請(qǐng)注意兩個(gè)問(wèn)題。我們用傳址方式傳遞參數(shù),你可以從 '&' (取地址)操作符看出來(lái)。這把變 量的機(jī)器地址傳遞給了函數(shù),因此函數(shù)自己就能改變變量的值。也可以使用全 局變量編寫(xiě)程序,但是應(yīng)該盡量少使用全局變量。指針是C的一個(gè)重要組成部分,你應(yīng)該充分地理解它。我們?cè)谝粋€(gè)新的.c文件中使用函數(shù)調(diào)用。操作系統(tǒng)總是尋找名為'main'的函 數(shù),并從那里開(kāi)始執(zhí)行。shuffle
13、.c中沒(méi)有'main'函數(shù),因此不能編譯為獨(dú)立的可 執(zhí)行文件。我們必須把它與另一個(gè)具有'main'函數(shù)并調(diào)用'shuffle'的程序組合起 來(lái)。運(yùn)行命令gcc -c shuffle.c并確定它創(chuàng)建了一個(gè)名為 shuffle.o 的新文件。編輯 game.c 文件,在第 7 行,在 deck_t 類(lèi)型的變量 deck 聲明之后,加上下面這一行:shuffle(&deck);現(xiàn)在,如果我們還象以前一樣創(chuàng)建可執(zhí)行文件,我們就會(huì)得到一個(gè)錯(cuò)誤gcc -o game game.c/tmp/ccmiHnJX.o:In fun cti on 'm
14、a in':/tmp/ccmiHnJX.o(.text+0xf):undefined reference to 'shuffle'collect2: ld returned 1 exit status編譯成功了,因?yàn)槲覀兊恼Z(yǔ)法是正確的。但是連接步驟卻失敗了,因?yàn)槲?們沒(méi)有告訴編譯器'shuffle'函數(shù)在哪里。那么,到底什么是連接?我們?cè)鯓痈嬖V 編譯器到哪里尋找這個(gè)函數(shù)呢?連接連接器Id,使用下面的命令,接受前面由as創(chuàng)建的目標(biāo)文件并把它轉(zhuǎn)換為 可執(zhí)行文件 gcc -o game game.o shuffle.o這將把兩個(gè)目標(biāo)文件組合起來(lái)并創(chuàng)建可執(zhí)行文件
15、 game。連接器從 shuffle.o 目標(biāo)文件中找到 shuffle 函數(shù),并把它包括進(jìn)可執(zhí)行文 件。目標(biāo)文件的真正好處在于,如果我們想再次使用那個(gè)函數(shù),我們所要做的 就是包含"deck.h"文件并把shuffle.o目標(biāo)文件連接到新的可執(zhí)行文件中。象這樣的代碼重用是經(jīng)常發(fā)生的。雖然我們并沒(méi)有編寫(xiě)前面作為調(diào)試語(yǔ)句 調(diào)用的printf函數(shù),連接器卻能從我們用#include<stdlib.h>語(yǔ)句包含的文件中找 到它的聲明,并把存儲(chǔ)在 C庫(kù)(/lib/libc.so.6 )中的目標(biāo)代碼連接進(jìn)來(lái)。這種方 式使我們可以使用已能正確工作的其他人的函數(shù),只關(guān)心我們所要
16、解決的問(wèn) 題。這就是為什么頭文件中一般只含有數(shù)據(jù)和函數(shù)聲明,而沒(méi)有函數(shù)體。一 般,你可以為連接器創(chuàng)建目標(biāo)文件或函數(shù)庫(kù),以便連接進(jìn)可執(zhí)行文件。我們的 代碼可能產(chǎn)生問(wèn)題,因?yàn)樵陬^文件中我們沒(méi)有放入任何函數(shù)聲明。為了確保一 切順利,我們還能做什么呢?另外兩個(gè)重要選項(xiàng)-Wall 選項(xiàng)可以打開(kāi)所有類(lèi)型的語(yǔ)法警告,以便幫助我們確定代碼是正確 的,并且盡可能實(shí)現(xiàn)可移植性。當(dāng)我們使用這個(gè)選項(xiàng)編譯我們的代碼時(shí),我們 將看到下述警告:game.c:9: warning:implicit declarati on of fun cti on 'shuffle'這讓我們知道還有一些工作要做。我們需要在
17、頭文件中加入一行代碼,以 便告訴編譯器有關(guān) shuffle 函數(shù)的一切,讓它可以做必要的檢查。聽(tīng)起來(lái)象是一 種狡辯,但這樣做可以把函數(shù)的定義與實(shí)現(xiàn)分離開(kāi)來(lái),使我們能在任何地方使 用我們的函數(shù),只要包含新的頭文件并把它連接到我們的目標(biāo)文件中就可以 了。下面我們就把這一行加入 deck.h 中。void shuffle(deck_t *pdeck);這就可以消除那個(gè)警告信息了。另一個(gè)常用編譯器選項(xiàng)是優(yōu)化選項(xiàng)-0#(即-02)。這是告訴編譯器你需要什么 級(jí)別的優(yōu)化。編譯器具有一整套技巧可以使你的代碼運(yùn)行得更快一點(diǎn)。對(duì)于象我們這種 小程序,你可能注意不到差別,但對(duì)于大型程序來(lái)說(shuō),它可以大幅度提高運(yùn)行
18、速度。你會(huì)經(jīng)常碰到它,所以你應(yīng)該知道它的意思。調(diào)試我們都知道,代碼通過(guò)了編譯并不意味著它按我們得要求工作了。你可以使用下面的命令驗(yàn)證是否所有的號(hào)碼都被使用了game | sort - n | less并且檢查有沒(méi)有遺漏。如果有問(wèn)題我們?cè)撛趺崔k?我們?nèi)绾尾拍苌钊氲讓?查找錯(cuò)誤呢?你可以使用調(diào)試器檢查你的代碼。大多數(shù)發(fā)行版都提供著名的調(diào) 試器:gdb。如果那些眾多的命令行選項(xiàng)讓你感到無(wú)所適從,那么你可以使用KDE提供的一個(gè)很好的前端工具 KDbgo還有一些其它的前端工具,它們都很相似。 要開(kāi)始調(diào)試,你可以選擇File->Executable然后找到你的game程序。當(dāng)你按下 F5鍵或選擇Ex
19、ecution->從菜單運(yùn)行時(shí),你可以在另一個(gè)窗口中看到輸出。怎么 回事?在那個(gè)窗口中我們什么也看不到。不要擔(dān)心,KDbg沒(méi)有出問(wèn)題。問(wèn)題在于我們?cè)诳蓤?zhí)行文件中沒(méi)有加入任何調(diào)試信息,所以KDbg不能告訴我們內(nèi)部發(fā)生了什么。編譯器選項(xiàng) -g 可以把必要的調(diào)試信息加入目標(biāo)文件。你必須用這個(gè)選項(xiàng)編 譯目標(biāo)文件(擴(kuò)展名為.0),所以命令行成了:gcc -g -c shuffle.c game.cgcc -g -o game game.o shuffle.o這就把鉤子放入了可執(zhí)行文件,使 gdb和KDbg能指出運(yùn)行情況。調(diào)試是一 種很重要的技術(shù),很值得你花時(shí)間學(xué)習(xí)如何使用。調(diào)試器幫助程序員的方法是 它能在源代碼中設(shè)置 斷點(diǎn)”現(xiàn)在你可以用右鍵單擊調(diào)用 shuffle函數(shù)的那行代 碼,試著設(shè)置斷點(diǎn)。那一行邊上會(huì)出現(xiàn)一個(gè)紅色的小圓圈。現(xiàn)在當(dāng)你按下 F5鍵 時(shí),程序就會(huì)在那一行停止執(zhí)行。按 F8可以跳入shuffle函數(shù)。呵,我們現(xiàn)在可 以看到shuffle.c中的代碼了!我
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 鋼板合同協(xié)議書(shū)樣板圖
- 中國(guó)染布行業(yè)市場(chǎng)前景預(yù)測(cè)及投資價(jià)值評(píng)估分析報(bào)告
- 商務(wù)包車(chē)運(yùn)營(yíng)方案范文
- 麻辣燙創(chuàng)業(yè)計(jì)劃書(shū)例文
- 中國(guó)橡膠及橡膠制品項(xiàng)目投資計(jì)劃書(shū)
- 噴頭維修合同協(xié)議書(shū)
- 外賣(mài)超時(shí)計(jì)劃書(shū)
- 校園桌游創(chuàng)業(yè)計(jì)劃書(shū)模板
- 經(jīng)紀(jì)合同合作協(xié)議書(shū)
- 馬病復(fù)習(xí)資料
- 乳腺癌手術(shù)的整體治療
- 2023年陜西省普通高校職業(yè)教育單獨(dú)招生考試英語(yǔ)試題及答案
- 工程師轉(zhuǎn)正工作總結(jié)
- 8 推翻帝制 民族覺(jué)醒 說(shuō)課稿 -2023-2024學(xué)年道德與法治五年級(jí)下冊(cè)統(tǒng)編版
- 麗聲北極星分級(jí)繪本第二級(jí)下-
- 變電站數(shù)字孿生框架構(gòu)建與關(guān)鍵技術(shù)研究
- 2025-2030年中國(guó)報(bào)廢汽車(chē)回收行業(yè)市場(chǎng)十三五發(fā)展規(guī)劃及投資戰(zhàn)略研究報(bào)告新版
- DIP支付下的病案首頁(yè)填寫(xiě)
- 兩癌防治知識(shí)培訓(xùn)
- KCA試題庫(kù)完整版
- 2025屆湖南省高考化學(xué)第一輪復(fù)習(xí)模擬選擇題-化學(xué)與生活43道(附答案)
評(píng)論
0/150
提交評(píng)論