L1嵌入式Linux C語(yǔ)言開發(fā)環(huán)境_第1頁(yè)
L1嵌入式Linux C語(yǔ)言開發(fā)環(huán)境_第2頁(yè)
L1嵌入式Linux C語(yǔ)言開發(fā)環(huán)境_第3頁(yè)
L1嵌入式Linux C語(yǔ)言開發(fā)環(huán)境_第4頁(yè)
L1嵌入式Linux C語(yǔ)言開發(fā)環(huán)境_第5頁(yè)
已閱讀5頁(yè),還剩205頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第二章Linux程序設(shè)計(jì)基礎(chǔ)—C環(huán)境主講付明磊第一講Linux編程概述文本編輯器viLinux下函數(shù)庫(kù)GCC及其使用Linux編程概述

Linux軟件開發(fā)一直在Internet環(huán)境下講行。這個(gè)環(huán)境是全球性的,編程人員來自世界各地。只要能夠訪問Web站點(diǎn),就可以啟動(dòng)一個(gè)以Linux為基礎(chǔ)的軟件項(xiàng)目。Linux編程概述Linux開發(fā)工作經(jīng)常是在Linux用戶決定共同完成一個(gè)項(xiàng)目時(shí)開始的。當(dāng)開發(fā)工作完成后,該軟件就被放到Internet站點(diǎn)上,任何用戶都可以訪問和下載它。由于這個(gè)活躍的開發(fā)環(huán)境,新的以Linux為基礎(chǔ)的軟件功能日益強(qiáng)大,而且呈現(xiàn)爆炸式的增長(zhǎng)態(tài)勢(shì)。Linux編程概述大多數(shù)Linux軟件是經(jīng)過自由軟件基金會(huì)(FreeSoftwareFoundation)提供的GNU(GNU即GNU’snotUNIX)公開認(rèn)證授權(quán)的,因而通常被稱作GNU軟件。GNU軟件免費(fèi)提供給用戶使用,并被證明是非??煽亢透咝У摹TS多流行的Linux實(shí)用程序如C編譯器、shell和編輯器都是GNU軟件應(yīng)用程序。

Linux程序需要首先轉(zhuǎn)化為低級(jí)機(jī)器語(yǔ)言即所謂的二進(jìn)制代碼以后,才能被操作系統(tǒng)執(zhí)行。例如編程時(shí),先用普通的編程語(yǔ)言生成一系列指令,這些指令可被翻譯為適當(dāng)?shù)目蓤?zhí)行應(yīng)用程序的二進(jìn)制代碼。這個(gè)翻譯過程可由解釋器一步步來完成,或者也可以立即由編譯器明確地完成。Linux編程概述Linux編程概述Shell編程語(yǔ)言如BASH、TCSH、GAWK、Perl、Tcl和Tk都利用自己的解釋器。用這些語(yǔ)言編制的程序盡管是應(yīng)用程序文件,但可以直接運(yùn)行。編譯器則不同,它將生成一個(gè)獨(dú)立的二進(jìn)制代碼文件然后才可以運(yùn)行。Linux編程風(fēng)格GNU風(fēng)格Linux內(nèi)核編程風(fēng)格

GNU風(fēng)格函數(shù)返回類型說明和函數(shù)名分兩行放置,函數(shù)起始字符和函數(shù)開頭左花括號(hào)放到最左邊。盡量不要讓兩個(gè)不同優(yōu)先級(jí)的操作符出現(xiàn)在相同的對(duì)齊方式中,應(yīng)該附加額外的括號(hào)使得代碼縮進(jìn)可以表示出嵌套。

GNU風(fēng)格按照如下方式排版do-while語(yǔ)句:do{}while()每個(gè)程序都應(yīng)該以一段簡(jiǎn)短的說明其功能的注釋開頭。

GNU風(fēng)格請(qǐng)為每個(gè)函數(shù)書寫注釋,說明函數(shù)是做什么的,需要哪些入口參數(shù),參數(shù)可能值的含義和用途。如果用了非常見的、非標(biāo)準(zhǔn)的東西,或者可能導(dǎo)致函數(shù)不能工作的任何可能的值,應(yīng)該進(jìn)行特殊說明。如果存在重要的返回值,也需要說明。不要聲明多個(gè)變量時(shí)跨行,每一行都以一個(gè)新的聲明開頭。GNU風(fēng)格當(dāng)一個(gè)if中嵌套了另一個(gè)if-else時(shí),應(yīng)用花括號(hào)把if-else括起來。要在同一個(gè)聲明中同時(shí)說明結(jié)構(gòu)標(biāo)識(shí)和變量或者結(jié)構(gòu)標(biāo)識(shí)和類型定義(typedef)。先定義變量,再使用。盡量避免在if的條件中進(jìn)行賦值。

GNU風(fēng)格請(qǐng)?jiān)诿种惺褂孟聞澗€以分割單詞,盡量使用小寫;把大寫字母留給宏和枚舉常量,以及根據(jù)統(tǒng)一慣例使用的前綴。例如,應(yīng)該使用類似ignore_space_change_flag的名字;不要使用類似iCantReadThis的名字。用于表明一個(gè)命令行選項(xiàng)是否給出的變量應(yīng)該在選項(xiàng)含義的說明之后,而不是選項(xiàng)字符之后被命名。Linux內(nèi)核編程風(fēng)格Linux內(nèi)核縮進(jìn)風(fēng)格是8個(gè)字符。Linux內(nèi)核風(fēng)格采用K&R標(biāo)準(zhǔn),將開始的大括號(hào)放在一行的最后,而將結(jié)束的大括號(hào)放在一行的第一位。Linux內(nèi)核編程風(fēng)格命名盡量簡(jiǎn)潔。不應(yīng)該使用諸如ThisVariableIsATemporaryCounter之類的名字。應(yīng)該命名為tmp,這樣容易書寫,也不難理解。命名全局變量,應(yīng)該用描述性命名方式,例如應(yīng)該命名“count_active_users()”,而不是“cntusr()”。本地變量應(yīng)該避免過長(zhǎng)。

Linux內(nèi)核編程風(fēng)格函數(shù)最好短小精悍,一般來說不要讓函數(shù)的參數(shù)多于10個(gè),否則應(yīng)該嘗試分解這個(gè)過于復(fù)雜的函數(shù)。通常情況,注釋說明代碼的功能,而不是其實(shí)現(xiàn)原理。避免把注釋插到函數(shù)體內(nèi),而寫到函數(shù)前面,說明其功能,如果這個(gè)函數(shù)的確很復(fù)雜,其中需要有部分注釋,可以寫些簡(jiǎn)短的注釋來說明那些重要的部分,但是不能過多。2.文本編輯器vivi的模式vi的進(jìn)入命令模式插入模式末行模式vi的模式CommandMode(命令模式)這是執(zhí)行vi后的缺省模式此時(shí)鍵盤輸入當(dāng)作命令命令有大小寫之區(qū)分InputMode(插入模式)使用a、i、o、c、r、s

進(jìn)入插入模式用戶輸入的任何字符都被vi當(dāng)做文件內(nèi)容保存起來,并將其顯示在屏幕上按下ESC鍵即可回到CommandModevi的模式LastMode(末行模式)在

CommandLine按下“:”即可進(jìn)入該模式用來進(jìn)行保存文件、打開文檔或環(huán)境的設(shè)定命令有大小寫之分vi的三種模式vi的進(jìn)入和內(nèi)容輸入進(jìn)入:

vi文件名輸入文件內(nèi)容(進(jìn)入插入模式)新增(append)

a從光標(biāo)所在位置后面開始新增內(nèi)容

A從光標(biāo)所在行最后面的地方開始新增內(nèi)容。

插入(insert)

i從光標(biāo)所在位置前面開始插入內(nèi)容

I從光標(biāo)所在行的第一個(gè)非空白字符前面開始插入資料。

開始(open)

o在光標(biāo)所在行下新增一行并進(jìn)入輸入模式。

O在光標(biāo)所在行上新增一行并進(jìn)入輸入模式。

(命令模式)vi的進(jìn)入命令模式1、光標(biāo)的移動(dòng)

h左移一個(gè)字符l

右移一個(gè)字符

j下移一行k

上移一行

w,W

跳至后一個(gè)字的開頭(W忽略標(biāo)點(diǎn))

b,B跳至前一個(gè)字的開頭(B忽略標(biāo)點(diǎn))

e

移動(dòng)到后一個(gè)字的末尾

^至本行第一個(gè)非空字符

$至行尾0至行首

H

移動(dòng)到當(dāng)前窗口的第一列

M

移動(dòng)到當(dāng)前窗口的中間列

L

移動(dòng)到視窗的最后一列

)光標(biāo)所在位置到下個(gè)句子的第一個(gè)字母

(光標(biāo)所在位置到該句子的第一個(gè)字母}光標(biāo)所在位置到該段落的最后一個(gè)字母

{光標(biāo)所在位置到該段落的第一個(gè)字母命令模式1、光標(biāo)的移動(dòng)(續(xù))nH

將光標(biāo)移到屏幕的第n行nL

將光標(biāo)移到屏幕的倒數(shù)第n行CTRL-d向下半頁(yè)CTRL-f向下一頁(yè)CTRL-u向上半頁(yè)CTRL-b向上一頁(yè)n-

減號(hào)移動(dòng)到上一行的第一個(gè)非空白字符,前面加上數(shù)字可以指定移動(dòng)到以上n行n+

加號(hào)移動(dòng)到下一行的第一個(gè)非空白字符,前面加上數(shù)字可以指定移動(dòng)到以下n行命令模式2、刪除

x

刪除光標(biāo)所在字符

X刪除光標(biāo)前面的字符

s刪除光標(biāo)所在字符,并進(jìn)入輸入模式

S

刪除光標(biāo)所在的行,并進(jìn)入輸入模式

dd

刪除光標(biāo)所在的行

D從光標(biāo)位置開始刪除到行尾

d與光標(biāo)移動(dòng)命令的組合命令模式3、修改

r

修改光標(biāo)所在字符,r后接著要修改的字符。如,rc

可以用字符“c”替換光標(biāo)所指向的當(dāng)前字符

R進(jìn)入替換狀態(tài),新增內(nèi)容會(huì)覆蓋原先內(nèi)容,直到按[ESC]回到命令模式下為止

cc修改光標(biāo)所在行

C修改從光標(biāo)位置到該行末尾的內(nèi)容

c與光標(biāo)移動(dòng)命令的組合命令模式4、復(fù)制和移動(dòng)yy

復(fù)制當(dāng)前行到內(nèi)存緩沖區(qū)nyy

復(fù)制n行內(nèi)容到內(nèi)存緩沖區(qū)

y與光標(biāo)移動(dòng)的組合p將緩沖區(qū)的內(nèi)容粘貼到光標(biāo)的后面P將緩沖區(qū)的內(nèi)容粘貼到光標(biāo)的前面另:在末行模式下實(shí)現(xiàn)移動(dòng):n1,n2mn3:把n1到n2行內(nèi)容搬到第n3行后5、搜索字符串/pattern

移至下一個(gè)包含pattern的行?pattern移至上一個(gè)包含pattern的行/往下重復(fù)查找?往上重復(fù)查找n

在同一方向重復(fù)查找N

在相反方向重復(fù)查找/pattern/+n

移至下一個(gè)pattern所在行后的第n行?pattern?-n移至上一個(gè)Pattern所在行前的第n行*.$^[]{}\/包含在查找字符串中,要用轉(zhuǎn)義字符(\)命令模式末行模式1、文件的保存和退出:w保存:q退出:w!強(qiáng)制保存:q!強(qiáng)制退出:wq

保存退出:wq!強(qiáng)制保存退出文件的保存和退出末行模式2、字符串的替換

:s/str1/str2/用字符串str2替換行中首次出現(xiàn)的字符串str1:s/str1/str2/g用字符串str2替換行中所有出現(xiàn)的字符串str1:.,$s/str1/str2/g用字符串str2替換正文當(dāng)前行到末尾所有出現(xiàn)的字符串str1:1,$s/str1/str2/g用字符串str2替換正文中所有出現(xiàn)的字符串str1:g/str1/s//str2/g功能同上

末行模式其他::n將光標(biāo)移到第n行編輯多個(gè)文件vifile1file2…:n編輯下一個(gè)文件:efilename編輯指定文件Linux下函數(shù)庫(kù)一個(gè)“程序函數(shù)庫(kù)”就是一個(gè)文件包含了一些編譯好的代碼和數(shù)據(jù),這些編譯好的代碼和數(shù)據(jù)可以在事后供其他的程序使用。程序函數(shù)庫(kù)可以使整個(gè)程序更加模塊化,更容易重新編譯,而且更方便升級(jí)。Linux下函數(shù)庫(kù)可分為兩種類型:靜態(tài)函數(shù)庫(kù)(staticlibraries):是一個(gè)普通的目標(biāo)文件的集合,一般用“.a”作為文件的后綴。靜態(tài)函數(shù)庫(kù)和共享函數(shù)庫(kù)相比有很多的缺點(diǎn),占用內(nèi)存空間多。但使用ELF格式的靜態(tài)庫(kù)函數(shù)生成的代碼可以比使用共享函數(shù)庫(kù)的程序運(yùn)行速度上快一些。可以用ar這個(gè)程序來創(chuàng)建一個(gè)靜態(tài)函數(shù)庫(kù)文件,或者往一個(gè)已經(jīng)存在地靜態(tài)函數(shù)庫(kù)文件添加新的目標(biāo)代碼。例如,把file1.o和file2.o加入到my_library.a這個(gè)函數(shù)庫(kù)文件:

ar

rcs

my_library.afile1.ofile2.o

然后運(yùn)行ranlib,以給庫(kù)加入一些索引信息Linux下函數(shù)庫(kù)共享函數(shù)庫(kù)(sharedlibraries):當(dāng)一個(gè)可執(zhí)行程序在啟動(dòng)的時(shí)候被加載的函數(shù)。每個(gè)共享函數(shù)庫(kù)都有個(gè)特殊的名字,稱作“soname”。soname名字命名必須以“l(fā)ib”作為前綴,然后是函數(shù)庫(kù)的名字,然后是“.so”,最后是版本號(hào)信息。Linux下函數(shù)庫(kù)優(yōu)點(diǎn):多進(jìn)程使用同一函數(shù)庫(kù);修改函數(shù)庫(kù)不需重新連編。安裝一個(gè)新版本的函數(shù)庫(kù)的時(shí)候,要先將這些函數(shù)庫(kù)文件拷貝到一些特定的目錄中,運(yùn)行l(wèi)dconfig就可以。ldconfig檢查已經(jīng)存在的庫(kù)文件,然后創(chuàng)建soname的符號(hào)鏈接到真正的函數(shù)庫(kù),同時(shí)設(shè)置/etc/ld.so.cache這個(gè)緩沖文件。

Linux下函數(shù)庫(kù)例如,創(chuàng)建兩個(gè)目標(biāo)文件(a.o和b.o),然后創(chuàng)建一個(gè)包含a.o和b.o的共享函數(shù)庫(kù)。gcc-fPIC-g-c-Walla.cgcc-fPIC-g-c-Wallb.cgcc-shared-Wl,-soname,liblusterstuff.so.1-oliblusterstuff.so.1.0.1a.o

b.o–lc注:”-fPIC”是位置無關(guān)參數(shù),”-g”和“-Wall”參數(shù)不是必須的。Linux下函數(shù)庫(kù)函數(shù)庫(kù)和頭文件的保存位置a.函數(shù)庫(kù)/lib:系統(tǒng)必備共享函數(shù)庫(kù)/usr/lib:標(biāo)準(zhǔn)共享函數(shù)庫(kù)和靜態(tài)函數(shù)庫(kù)/usr/i486-linux-libc5/lib:libc5兼容性函數(shù)庫(kù)/usr/X11R6/lib:X11R6的函數(shù)庫(kù)/usr/local/lib:本地函數(shù)庫(kù)

b.頭文件/usr/include:系統(tǒng)頭文件/usr/local/include:本地頭文件c.

共享函數(shù)庫(kù)的相關(guān)配置和命令/etc/ld.so.conf:包含共享庫(kù)的搜索位置ldconfig:共享庫(kù)管理工具,一般在更新了共享庫(kù)之后要運(yùn)行該命令ldd:可查看可執(zhí)行文件所使用的共享函數(shù)庫(kù)使用GNUcc開發(fā)應(yīng)用程序gcc的簡(jiǎn)介可執(zhí)行文件的格式gcc的使用

gcc簡(jiǎn)介gcc是GNU的C和C++編譯器。實(shí)際上,gcc能夠編譯多種語(yǔ)言:C、C++和ObjectC等。利用gcc

命令可同時(shí)編譯并連接C和C++源程序。也可以對(duì)幾個(gè)C源文件利用gcc

編譯、連接并生成可執(zhí)行文件。gcc可以使程序員靈活地控制編譯過程。編譯過程一般可以分為下面四個(gè)階段,每個(gè)階段分別調(diào)用不同的工具進(jìn)行處理預(yù)處理鏈接編譯組譯源程序(*.c)可執(zhí)行文件預(yù)處理器編譯器組譯器連接器gcc的四個(gè)階段命令gcc首先調(diào)用cpp進(jìn)行預(yù)處理,在預(yù)處理過程中,對(duì)源代碼文件中的文件包含(include)、預(yù)編譯語(yǔ)句(如宏定義define等)進(jìn)行分析。接著調(diào)用cc1進(jìn)行編譯,這個(gè)階段根據(jù)輸入文件生成以.o為后綴的目標(biāo)文件。gcc的四個(gè)階段匯編過程是針對(duì)匯編語(yǔ)言的步驟,調(diào)用as進(jìn)行工作,一般來講,.S或.s為后綴的匯編語(yǔ)言源代碼文件匯編之后都生成以.o為后綴的目標(biāo)文件。當(dāng)所有的目標(biāo)文件都生成之后,gcc就調(diào)用ld來完成最后的關(guān)鍵性工作,這個(gè)階段就是連接。在連接階段,所有的目標(biāo)文件被安排在可執(zhí)行程序中的恰當(dāng)?shù)奈恢?,同時(shí),該程序所調(diào)用到的庫(kù)函數(shù)也從各自所在的函數(shù)庫(kù)中連到合適的地方??蓤?zhí)行文件格式

Linux系統(tǒng)中可執(zhí)行文件有兩種格式。第一種格式是a.out格式,這種格式用于早期的Linux系統(tǒng)以及Unix系統(tǒng)的原始格式。a.out來自于UnixC編譯程序默認(rèn)的可執(zhí)行文件名。當(dāng)使用共享庫(kù)時(shí),a.out格式就會(huì)發(fā)生問題。把a(bǔ).out格式調(diào)整為共享庫(kù)是一種非常復(fù)雜的操作??蓤?zhí)行文件格式因此,一種新的文件格式被引入U(xiǎn)nix系統(tǒng)5的第四版本和Solaris系統(tǒng)中。它被稱為可執(zhí)行和連接的格式(ELF)。這種格式很容易實(shí)現(xiàn)共享庫(kù)。ELF格式已經(jīng)被Linux系統(tǒng)作為標(biāo)準(zhǔn)的格式采用??蓤?zhí)行文件格式gcc編譯程序產(chǎn)生的所有的二進(jìn)制文件都是ELF格式的文件(即使可執(zhí)行文件的默認(rèn)名仍然是a.out)。較舊的a.out格式的程序仍然可以運(yùn)行在支持ELF格式的系統(tǒng)上。GNUC的使用基本語(yǔ)法

gcc[options][filenames]說明:在gcc后面可以有多個(gè)編譯選項(xiàng),同時(shí)進(jìn)行多個(gè)編譯操作。很多的gcc選項(xiàng)包括一個(gè)以上的字符。因此你必須為每個(gè)選項(xiàng)指定各自的連字符。例如,下面的兩個(gè)命令是不同的:

gcc-p-gtest1.c

gcc-pgtest1.c當(dāng)你不用任何選項(xiàng)編譯一個(gè)程序時(shí),GCC將會(huì)建立(假定編譯成功)一個(gè)名為a.out的可執(zhí)行文件。gcc選項(xiàng)-o選項(xiàng)你能用-o編譯選項(xiàng)來為將產(chǎn)生的可執(zhí)行文件指定一個(gè)文件名來代替a.out。

例:gcc–ocountcount.c-c選項(xiàng):告訴GCC僅把源代碼編譯為目標(biāo)代碼而跳過匯編和連接的步驟。這個(gè)選項(xiàng)使用的非常頻繁,因?yàn)樗沟镁幾g多個(gè)C程序時(shí)速度更快并且更易于管理。缺省時(shí)GCC建立的目標(biāo)代碼文件有一個(gè).o的擴(kuò)展名。 例:gcc–ctest2.c-E只運(yùn)行C預(yù)編譯器。-S編譯選項(xiàng)告訴gcc在為C代碼產(chǎn)生了匯編語(yǔ)言文件后停止編譯。-shared生成共享目標(biāo)文件。通常用在建立共享庫(kù)時(shí)。-static禁止使用共享連接。警告選項(xiàng)在gcc中用開關(guān)-Wall控制警告信息,使用示例命令如下:

gcc–Wall-otest3_1test3_1.c-w不生成任何警告信息。

查找選項(xiàng)gcc一般使用默認(rèn)路徑查找頭文件和庫(kù)文件。如果文件所用的頭文件或庫(kù)文件不在缺省目錄下,則編譯時(shí)要指定它們的查找路徑。-I選項(xiàng):指定頭文件的搜索目錄 例:

gcc–I/export/home/st–otest1test1.c-L選項(xiàng):指定庫(kù)文件的搜索目錄 例:

gcc–L/usr/X11/R6/lib–otest1test1.c多個(gè)源文件生成一個(gè)可執(zhí)行文件

問題:有多個(gè)源文件時(shí),如何生成一個(gè)可執(zhí)行文件?方法1:gcc–Wall–omytesttest1.ctest2.ctest3.c方法2:gcc-Wall-ctest1.cgcc-Wall–ctest2.cgcc-Wall–ctest3.cgcc–omytesttest1.otest2.otest3.o優(yōu)化選項(xiàng)優(yōu)化選項(xiàng)可以使GCC在耗費(fèi)更多編譯時(shí)間和犧牲易調(diào)試性的基礎(chǔ)上產(chǎn)生更小更快的可執(zhí)行文件。這些選項(xiàng)中最典型的是-O和-O2選項(xiàng)。-O0不進(jìn)行優(yōu)化處理。-O選項(xiàng):告訴GCC對(duì)源代碼進(jìn)行基本優(yōu)化。這些優(yōu)化在大多數(shù)情況下都會(huì)使程序執(zhí)行的更快。-O2選項(xiàng):告訴GCC產(chǎn)生盡可能小和盡可能快的代碼。-O2選項(xiàng)將使編譯的速度比使用-O時(shí)慢。但通常產(chǎn)生的代碼執(zhí)行速度會(huì)更快。-O3選項(xiàng):比-O2更進(jìn)一步優(yōu)化,包括inline函數(shù)。

版本選項(xiàng)-v選項(xiàng)用戶將會(huì)得到自己目前正在使用的gcc的版本及與版本相關(guān)的一些信息。

gcc-v將得到如下結(jié)果:

Readingspecsfrom/usr/lib/gcc-lib/i486-box-linux/2.7.2/specsgccversion2.7.2-V選項(xiàng)如果安裝了多個(gè)版本的gcc,并且想強(qiáng)制執(zhí)行其中的某個(gè)版本,可以用命令通知系統(tǒng)用戶要使用的版本。

gcc-V2.6.3-v宏定義選項(xiàng)-DMACRO以字符串“1”定義MACRO宏。-DMACRO=DEFN以字符串“DEFN”定義MACRO宏。-UMACRO取消對(duì)MACRO宏的定義。

調(diào)試和剖析選項(xiàng)

使用調(diào)試選項(xiàng)后,gcc在進(jìn)行編譯的時(shí)候,在目標(biāo)文件(.o)和創(chuàng)建的可執(zhí)行文件中插入額外信息,這些額外信息使gdb能夠判斷編譯過的代碼和源代碼之間的關(guān)系。-g選項(xiàng):告訴GCC產(chǎn)生能被GNU調(diào)試器使用的調(diào)試信息以便調(diào)試你的程序。 例:gcc–g–otest3test3.c-pg選項(xiàng):告訴GCC在你的程序里加入額外的代碼,執(zhí)行時(shí),產(chǎn)生gprof用的剖析信息以顯示你的程序的耗時(shí)情況。使用gdb調(diào)試工具,命令行如下: 例:gcc–ggdb3–otest3test3.cGcc實(shí)例調(diào)試基本知識(shí)總結(jié)與回顧兩個(gè)實(shí)例gcc編譯流程分析1、預(yù)處理階段:該階段編譯器將上述代碼中的stdio.h編譯出來,并且用戶可以使用Gcc的選項(xiàng)“-E”進(jìn)行查看,該選項(xiàng)讓Gcc在進(jìn)行完預(yù)處理后停止下來。.i文件是已經(jīng)過預(yù)處理的C的源程序

2、編譯階段:該階段檢查代碼的規(guī)范性,是否有語(yǔ)法錯(cuò)誤等,確定代碼實(shí)際要做的工作,檢查無誤后,Gcc把代碼翻譯成匯編語(yǔ)言??梢杂谩?S”選項(xiàng)來查看,只進(jìn)行編譯不匯編,生成匯編代碼。3、匯編階段:把編譯階段生成的“.s”文件轉(zhuǎn)化為目標(biāo)文件,可用“-c”選項(xiàng)查看,將匯編代碼轉(zhuǎn)化為.o的2進(jìn)制目標(biāo)代碼。

4、鏈接階段:設(shè)計(jì)到重要的概念函數(shù)庫(kù)。Stdio.h中并沒有printf()的實(shí)現(xiàn),它的實(shí)現(xiàn)都被做到libc.so.6的庫(kù)文件中去了,沒有特別指定時(shí),Gcc會(huì)在“/usr/lib”下面搜索,鏈接到libc.so.6,這樣就實(shí)現(xiàn)了printf(),這就是鏈接的作用。gcc支持的后綴名解釋c:C語(yǔ)言源代碼文件a:目標(biāo)文件構(gòu)成的庫(kù)文件C;.cc;.cpp:標(biāo)識(shí)C++源代碼h:頭文件i:標(biāo)識(shí)文件是已經(jīng)預(yù)處理過的C源代碼文件,一般為中間代碼文件ii:已經(jīng)預(yù)處理過的C++源代碼文件,也是中間代碼文件o:編譯后的目標(biāo)文件,源文件生成的中間目標(biāo)文件s:匯編語(yǔ)言源代碼文件S:經(jīng)過預(yù)編譯的匯編語(yǔ)言源代碼文件o:編譯以后的程序目標(biāo)文件,目標(biāo)文件經(jīng)過連接成為可執(zhí)行文件gcc編譯選項(xiàng)分析Gcc有超過100個(gè)可用選項(xiàng),主要包括總體選項(xiàng),告警和出錯(cuò)選項(xiàng),優(yōu)化選項(xiàng)和體系結(jié)構(gòu)選項(xiàng)

-c只編譯不鏈接,生成目標(biāo)文件“.o”。

-S只編譯不匯編,生成匯編代碼。

-E只進(jìn)行預(yù)編譯,不做其他處理。

-g在可執(zhí)行程序中包含標(biāo)準(zhǔn)調(diào)試信息。

-ofile把輸出文件輸出到file里。-v打印出編譯器內(nèi)部編譯各過程的命令行信息和編譯器的版本。

-I在頭文件搜索路徑列表中添加dir目錄(只指定路徑并沒有加具體的文件名)

-L在庫(kù)文件的搜索路徑列表中添加dir目錄

-static靜態(tài)鏈接庫(kù)

-librarry

鏈接名為library的庫(kù)文件頭文件和庫(kù)文件在include語(yǔ)句中”<>”表示在標(biāo)準(zhǔn)路徑中搜索頭文件,””表示在本目錄中搜索。頭文件和庫(kù)文件的關(guān)系:頭文件不一定是系統(tǒng)提供的,可以使用戶自己編寫的;而庫(kù)文件都是系統(tǒng)提供的。有一個(gè)特殊的選項(xiàng)“-l”,它指示gcc去鏈接庫(kù)文件libsunq.so由于在linux下的庫(kù)文件命名時(shí)有一個(gè)規(guī)定:必須以l,i,b,3個(gè)字母開頭,因此在用-l選項(xiàng)指定鏈接的庫(kù)文件名時(shí)可以省去l,i,b,也就是說在對(duì)“-lsunq”進(jìn)行處理時(shí),會(huì)自動(dòng)去鏈接Libsunq.so.gcc實(shí)例1終端vihello.chello.chello.hgcc–E–ohello.ihello.cgcc–S–ohello.s

hello.igcc–chello.s–ohello.ogcchello.o–ohello程序執(zhí)行結(jié)果預(yù)處理->編譯->匯編->鏈接源代碼通過gcc-E-oa.cxx

a.c

進(jìn)行預(yù)處理.生成預(yù)處理文件通過gcc-Sa.s

a.c

進(jìn)行編譯.生成匯編代碼文件通過gcc-c-oa.o

a.c

進(jìn)行匯編.生成目標(biāo)代碼通過gcca.o-oa.out

生成可執(zhí)行文件.gcc實(shí)例2-告警和出錯(cuò)選項(xiàng)-ansi

支持符合ANSI標(biāo)準(zhǔn)的C的程序-pedantic允許發(fā)出ANSIC標(biāo)準(zhǔn)所列的全部警告信息-pedantic-error允許發(fā)出ANSIC標(biāo)準(zhǔn)所列的全部錯(cuò)誤信息-w關(guān)閉所有告警-wall允許發(fā)出Gcc提供的所有有用的報(bào)警信息-werror

把所有告警信息轉(zhuǎn)化為錯(cuò)誤信息,并在告警信息發(fā)生時(shí),終止編譯過程gcc優(yōu)化選項(xiàng)gcc可以對(duì)代碼進(jìn)行優(yōu)化,他通過編譯選項(xiàng)“-On”來控制優(yōu)化代碼的生成,其中n代表優(yōu)化級(jí)別,對(duì)于不同版本的gcc來講,n的取之范圍及其對(duì)應(yīng)的優(yōu)化效果可能并不完全相同比較典型的是從0變化到2和3

-O主要進(jìn)行線程跳轉(zhuǎn)和延遲退棧兩種優(yōu)化

-O2出完成上述工作外,還進(jìn)行一些額外的調(diào)整工作,如處理器指令調(diào)度

-O3包括循環(huán)展開和其他一些與處理器特性相關(guān)的優(yōu)化工作gcc–Wallwrong.c–owronggcc–ansi

wrong.c–owronggcc–pedanticwrong.c–owrong第二講調(diào)試工具gdbGNUmake和makefile調(diào)試工具gdbGDB調(diào)試器簡(jiǎn)介gdb

的常用命令gdb應(yīng)用實(shí)例gdb簡(jiǎn)介L(zhǎng)inux系統(tǒng)中包含了GNU調(diào)試程序gdb,它是一個(gè)用來調(diào)試C和C++程序的調(diào)試器。可以使程序開發(fā)者在程序運(yùn)行時(shí)觀察程序的內(nèi)部結(jié)構(gòu)和內(nèi)存的使用情況。gdb

所提供的一些功能如下所示:運(yùn)行程序,設(shè)置所有的能影響程序運(yùn)行的參數(shù)和環(huán)境;控制程序在指定的條件下停止運(yùn)行;當(dāng)程序停止時(shí),可以檢查程序的狀態(tài);修改程序的錯(cuò)誤,并重新運(yùn)行程序;動(dòng)態(tài)監(jiān)視程序中變量的值;可以單步逐行執(zhí)行代碼,觀察程序的運(yùn)行狀態(tài)。分析崩潰程序的產(chǎn)生的core文件gdb的特點(diǎn)gdb的功能非常強(qiáng)大到目前為止,gdb已能夠支持Moduls-2、Chill、Pascal和FORTRAN程序的調(diào)試,但是調(diào)試這些語(yǔ)言的源程序時(shí)有一些功能還不能使用。例如調(diào)試FORTRAN程序時(shí)還不支持表達(dá)式的輸入、輸出變量或類FORTRAN的詞法。gdb程序調(diào)試的對(duì)象是可執(zhí)行文件,而不是程序的源代碼文件。

然而,并不是所有的可執(zhí)行文件都可以用gdb調(diào)試。如果要讓產(chǎn)生的可執(zhí)行文件可以用來調(diào)試,需在執(zhí)行g(shù)cc指令編譯程序時(shí),加上-g參數(shù),指定程序在編譯時(shí)包含調(diào)試信息。調(diào)試信息包含程序里的每個(gè)變量的類型和在可執(zhí)行文件里的地址映射以及源代碼的行號(hào)。gdb

利用這些信息使源代碼和機(jī)器碼相關(guān)聯(lián)。gdb是一個(gè)用來調(diào)試C和C++程序的常用調(diào)試工具之一。gdb的啟動(dòng)在命令行上輸入gdb并按回車鍵就可以運(yùn)行g(shù)db了,如果一切正常的話,將啟動(dòng)gdbgdb[filename]出現(xiàn)(gdb)在這里,可以輸入調(diào)試命令在可以使用gdb

調(diào)試程序之前,必須使用-g選項(xiàng)編譯源文件??稍趍akefile中如下定義CFLAGS變量:CFLAGS=-g運(yùn)行

獲取幫助信息啟動(dòng)gdb后,可以在命令行上指定很多的選項(xiàng)。輸入:help可以獲得gdb的幫助信息。如果想要了解某個(gè)具體命令(比如break)的幫助信息,在gdb提示符下輸入下面的命令:break屏幕上會(huì)顯示關(guān)于break的幫助信息。從返回的信息可知,break是用于設(shè)置斷點(diǎn)的命令。另一個(gè)獲得gdb幫助的方法是瀏覽gdb的手冊(cè)頁(yè)。在LinuxShell提示符輸入:mangdb可以看到man的手冊(cè)頁(yè)gdb命令的分類在gdb提示符處鍵入help,將列出命令的分類,主要的分類有:aliases:命令別名breakpoints:斷點(diǎn)定義;data:數(shù)據(jù)查看;files:指定并查看文件;internals:維護(hù)命令;running:程序執(zhí)行;

stack:調(diào)用棧查看;statu:狀態(tài)查看;tracepoints:跟蹤程序執(zhí)行。后跟命令的分類名,可獲得該類命令的詳細(xì)清單基本gdb命令(1/2)file命令:裝入想要調(diào)試的可執(zhí)行文件。cd命令:改變工作目錄。pwd命令:返回當(dāng)前工作目錄。run命令:執(zhí)行當(dāng)前被調(diào)試的程序。kill命令:停止正在調(diào)試的應(yīng)用程序。list命令:列出正在調(diào)試的應(yīng)用程序的源代碼。break命令:設(shè)置斷點(diǎn)。

watch命令:設(shè)置監(jiān)視點(diǎn),監(jiān)視表達(dá)式的變化。awatch命令:設(shè)置讀寫監(jiān)視點(diǎn)。當(dāng)要監(jiān)視的表達(dá)式被讀或?qū)憰r(shí)將應(yīng)用程序掛起。它的語(yǔ)法與watch命令相同。rwatch命令:設(shè)置讀監(jiān)視點(diǎn),當(dāng)監(jiān)視表達(dá)式被讀時(shí)將程序掛起,等侍調(diào)試。此命令的語(yǔ)法與watch相同。next命令:執(zhí)行下一條源代碼,但是不進(jìn)入函數(shù)內(nèi)部。也就是說,將一條函數(shù)調(diào)用作為一條語(yǔ)句執(zhí)行。執(zhí)行這個(gè)命令的前提是已經(jīng)run,開始了代碼的執(zhí)行?;緂db命令(2/2)step命令:執(zhí)行下一條源代碼,進(jìn)入函數(shù)內(nèi)部。如果調(diào)用了某個(gè)函數(shù),會(huì)跳到函數(shù)所在的代碼中等候一步步執(zhí)行。執(zhí)行這個(gè)命令的前提是已經(jīng)用run開始執(zhí)行代碼。display命令:在應(yīng)用程序每次停止運(yùn)行時(shí)顯示表達(dá)式的值。infobreak命令:顯示當(dāng)前斷點(diǎn)列表,包括每個(gè)斷點(diǎn)到達(dá)的次數(shù)16)infofiles命令:顯示調(diào)試文件的信息。17)infofunc命令:顯示所有的函數(shù)名。18)infolocal命令:顯示當(dāng)前函數(shù)的所有局部變量的信息。19)infoprog命令:顯示調(diào)試程序的執(zhí)行狀態(tài)。20)print命令;顯示表達(dá)式的值。21)delete命令:刪除斷點(diǎn)。指定一個(gè)斷點(diǎn)號(hào)碼,則刪除指定斷點(diǎn)。不指定參數(shù)則刪除所有的斷點(diǎn)。22)Shell命令:執(zhí)行LinuxShell命令。23)make命令:不退出gdb而重新編譯生成可執(zhí)行文件。24)Quit命令:退出gdb。gdb使用實(shí)例(1/2)/*一個(gè)有錯(cuò)誤的C源程序bugging.c*/#include<stdio.h>#include<stdlib.h>staticcharbuff[256];staticchar*string;intmain(){printf("Pleaseinputastring:");gets(string);printf("\nYourstringis:%s\n",string);}上面這個(gè)程序非常簡(jiǎn)單,其目的是接受用戶的輸入,然后將用戶的輸入打印出來。該程序使用了一個(gè)未經(jīng)過初始化的字符串地址string,因此,編譯并運(yùn)行之后,將出現(xiàn)SegmentFault錯(cuò)誤:$gcc-otest-gtest.c

$./testPleaseinputastring:asfdSegmentationfault(coredumped)gdb使用實(shí)例(2/2)為了查找該程序中出現(xiàn)的問題,我們利用gdb,并按如下的步驟進(jìn)行:1.運(yùn)行g(shù)dbbugging命令,裝入bugging可執(zhí)行文件;2.執(zhí)行裝入的bugging命令;3.使用where命令查看程序出錯(cuò)的地方;4.利用list命令查看調(diào)用gets函數(shù)附近的代碼;

5.唯一能夠?qū)е耮ets函數(shù)出錯(cuò)的因素就是變量string。用print命令查看string的值;6.在gdb

中,我們可以直接修改變量的值,只要將string取一個(gè)合法的指針值就可以了,為此,我們?cè)诘?1行處設(shè)置斷點(diǎn);7.程序重新運(yùn)行到第11行處停止,這時(shí),我們可以用setvariable命令修改string的取值;8.然后繼續(xù)運(yùn)行,將看到正確的程序運(yùn)行結(jié)果。

GNUmake和makefileGNUmake概述Makefile的基本結(jié)構(gòu)Makefile中的變量GNUmake的主要預(yù)定義變量Makefile的隱含規(guī)則make命令行選項(xiàng)使用automake和autoconf產(chǎn)生Makefile

GNUmake概述在大型的開發(fā)項(xiàng)目中,人們通常利用make工具來自動(dòng)完成編譯工作。這些工作包括:如果僅修改了某幾個(gè)源文件,則只重新編譯這幾個(gè)源文件;如果某個(gè)頭文件被修改了,則重新編譯所有包含該頭文件的源文件。利用這種自動(dòng)編譯可大大簡(jiǎn)化開發(fā)工作,避免不必要的重新編譯。

實(shí)際上,make工具通過一個(gè)稱為makefile

的文件來完成并自動(dòng)維護(hù)編譯工作。makefile

需要按照某種語(yǔ)法進(jìn)行編寫,其中說明了如何編譯各個(gè)源文件并連接生成可執(zhí)行文件,并定義了源文件之間的依賴關(guān)系。當(dāng)修改了其中某個(gè)源文件時(shí),如果其他源文件依賴于該文件,則也要重新編譯所有依賴該文件的源文件。默認(rèn)情況下,GNUmake工具在當(dāng)前工作目錄按如下順序搜索makefile:

GNUmakefile

makefile

Makefile

makefile舉例在UNIX中,習(xí)慣使用makefile作為makfile

文件。Linux程序員使用第三種文件名Makefile。因?yàn)榈谝粋€(gè)字母是大寫,通常被列在一個(gè)目錄的文件列表的最前面。如果要使用其他文件作為makefile,則可利用類似下面的make命令選項(xiàng)指定makefile

文件:

$make-fMakefile.debug

例1:一個(gè)簡(jiǎn)單的makefile

prog:prog1.oprog2.o

gccprog1.oprog2.o-oprog

prog1.o:prog1.clib.h

gcc-c-I.-oprog1.oprog1.c

prog2.o:prog2.c

gcc-cprog2.c

Makefile的基本結(jié)構(gòu)(1/2)Makefile是一個(gè)文本形式的數(shù)據(jù)庫(kù)文件,其中包含一些規(guī)則來告訴make處理哪些文件以及如何處理這些文件。規(guī)則主要是描述哪些文件(稱為target目標(biāo)文件,不要和編譯時(shí)產(chǎn)生的目標(biāo)文件相混淆)是從哪些別的文件(稱為dependency依賴文件)中產(chǎn)生的,以及用什么命令(command)來執(zhí)行這個(gè)過程。依靠這些信息,make會(huì)對(duì)磁盤上的文件進(jìn)行檢查,如果目標(biāo)文件的生成或被改動(dòng)時(shí)的時(shí)間(稱為該文件時(shí)間戳)至少比它的一個(gè)依賴文件還舊的話,make就執(zhí)行相應(yīng)的命令,以更新目標(biāo)文件。目標(biāo)文件不一定是最后的可執(zhí)行文件,可以是任何一個(gè)中間文件并可以作為其他目標(biāo)文件的依賴文件。Makefile的基本結(jié)構(gòu)(2/2)Makefile規(guī)則的一般形式如下:

target:dependencydependency (tab)<command>一個(gè)Makefile文件主要含有一系列的規(guī)則,每條規(guī)則包含以下內(nèi)容。一個(gè)目標(biāo)(target),即make最終需要?jiǎng)?chuàng)建的文件,如可執(zhí)行文件和目標(biāo)文件;目標(biāo)也可以是要執(zhí)行的動(dòng)作,如“clean”。一個(gè)或多個(gè)依賴文件(dependency)列表,通常是編譯目標(biāo)文件所需要的其他文件。一系列命今(command),是make執(zhí)行的動(dòng)作,通常是把指定的相關(guān)文件編譯成目標(biāo)文件的編譯命令,每個(gè)命令占一行,且每個(gè)命令行的起始字符必須為TAB字符。除非特別指定,否則make的工作目錄就是當(dāng)前目錄。target是需要?jiǎng)?chuàng)建的二進(jìn)制文件或目標(biāo)文件,dependency是在創(chuàng)建target時(shí)需要用到的一個(gè)或多個(gè)文件的列表,命令序列是創(chuàng)建target文件所需要執(zhí)行的步驟,比如編譯命令。Makefile實(shí)例(1/3)#以#開頭的為注釋行test:prog.ocode.o gcc–otestprog.ocode.o

prog.o:prog.c

prog.hcode.h

gcc–cprog.c–oprog.o

code.o:code.ccode.h

gcc–ccode.c–ocode.o

clean:

rm–f*.o上面的Makefile文件中共定義了四個(gè)目標(biāo):test、prog.o、code.o和clean。目標(biāo)從每行的最左邊開始寫,后面跟一個(gè)冒號(hào)(:),如果有與這個(gè)目標(biāo)有依賴性的其他目標(biāo)或文件,把它們列在冒號(hào)后面,并以空格隔開。然后另起一行開始寫實(shí)現(xiàn)這個(gè)目標(biāo)的一組命令。在Makefile中,可使用續(xù)行號(hào)(\)將一個(gè)單獨(dú)的命令行延續(xù)成幾行。但要注意在續(xù)行號(hào)(\)后面不能跟任何字符(包括空格和鍵)Makefile實(shí)例(2/3)一般情況下,調(diào)用make命令可輸入:#maketargettarget是Makefile文件中定義的目標(biāo)之一,如果省略target,make就將生成Makefile文件中定義的第一個(gè)目標(biāo)。對(duì)于上面Makefile的例子,單獨(dú)的一個(gè)“make”命令等價(jià)于:#maketest因?yàn)閠est是Makefile文件中定義的第一個(gè)目標(biāo),make首先將其讀入,然后從第一行開始執(zhí)行,把第一個(gè)目標(biāo)test作為它的最終目標(biāo),所有后面的目標(biāo)的更新都會(huì)影響到test的更新。第一條規(guī)則說明只要文件test的時(shí)間戳比文件prog.o或code.o中的任何一個(gè)舊,下一行的編譯命令將會(huì)被執(zhí)行。Makefile實(shí)例(3/3)但是,在檢查文件prog.o和code.o的時(shí)間戳之前,make會(huì)在下面的行中尋找以prog.o和code.o為目標(biāo)的規(guī)則,在第三行中找到了關(guān)于prog.o的規(guī)則,該文件的依賴文件是prog.c、prog.h和code.h。同樣,make會(huì)在后面的規(guī)則行中繼續(xù)查找這些依賴文件的規(guī)則,如果找不到,則開始檢查這些依賴文件的時(shí)間戳,如果這些文件中任何一個(gè)的時(shí)間戳比prog.o的新,make將執(zhí)行“gcc–cprog.c–oprog.o”命令,更新prog.o文件。以同樣的方法,接下來對(duì)文件code.o做類似的檢查,依賴文件是code.c和code.h。當(dāng)make執(zhí)行完所有這些套嵌的規(guī)則后,make將處理最頂層的test規(guī)則。如果關(guān)于prog.o和code.o的兩個(gè)規(guī)則中的任何一個(gè)被執(zhí)行,至少其中一個(gè).o目標(biāo)文件就會(huì)比test新,那么就要執(zhí)行test規(guī)則中的命令,因此make去執(zhí)行g(shù)cc命令將prog.o和code.o連接成目標(biāo)文件test。在上面Makefile的例子中,還定義了一個(gè)目標(biāo)clean,它是Makefile中常用的一種專用目標(biāo),即刪除所有的目標(biāo)模塊make的工作過程現(xiàn)在來看一下make做的工作:首先make按順序讀取makefile中的規(guī)則,然后檢查該規(guī)則中的依賴文件與目標(biāo)文件的時(shí)間戳哪個(gè)更新如果目標(biāo)文件的時(shí)問戳比依賴文件還早,就按規(guī)則中定義的命令更新目標(biāo)文件。如果該規(guī)則中的依賴文件又是其他規(guī)則中的目標(biāo)文件,那么依照規(guī)則鏈不斷執(zhí)行這個(gè)過程,直到Makefile文件的結(jié)束,至少可以找到一個(gè)不是規(guī)則生成的最終依賴文件,獲得此文件的時(shí)間戳然后從下到上依照規(guī)則鏈執(zhí)行目標(biāo)文件的時(shí)間戳比此文件時(shí)間戳舊的規(guī)則,直到最頂層的規(guī)則通過以上的分析過程,可以看到make的優(yōu)點(diǎn),因?yàn)?o目標(biāo)文件依賴.c源文件,源碼文件里一個(gè)簡(jiǎn)單改變都會(huì)造成那個(gè)文件被重新編譯,并根據(jù)規(guī)則鏈依次由下到上執(zhí)行編譯過程,直到最終的可執(zhí)行文件被重新連接。例如,當(dāng)改變一個(gè)頭文件的時(shí)候,由于所有的依賴關(guān)系都在Makefile里,因此不再需要記住依賴此頭文件的所有源碼文件,make可以自動(dòng)的重新編譯所有那些因依賴這個(gè)頭文件而改變了的源碼文件,如果需要,再進(jìn)行重新連接Makefile中的變量Makefile里的變量就像一個(gè)環(huán)境變量。事實(shí)上,環(huán)境變量在make中也被解釋成make的變量。這些變量對(duì)大小寫敏感,一般使用大寫宇母。幾乎可以從任何地方引用定義的變量,變量的主要作用如下:保存文件名列表。在前面的例子里,作為依賴文件的一些目標(biāo)文件名出現(xiàn)在可執(zhí)行文件的規(guī)則中,而在這個(gè)規(guī)則的命令行里同樣包含這些文件并傳遞給gcc做為命令參數(shù)。如果使用一個(gè)變量來保存所有的目標(biāo)文件名,則可以方便地加入新的目標(biāo)文件而且不易出錯(cuò)。

保存可執(zhí)行命令名,如編譯器。在不同的Linux系統(tǒng)中存在著很多相似的編譯器系統(tǒng),這些系統(tǒng)在某些地方會(huì)有細(xì)微的差別,如果項(xiàng)目被用在一個(gè)非gcc的系統(tǒng)里,則必須將所有出現(xiàn)編譯器名的地方改成用新的編譯器名。但是如果使用一個(gè)變量來代替編譯器名,那么只需要改變?cè)撟兞康闹?。其他所有地方的命令名就都改變了。保存編譯器的參數(shù)。在很多源代碼編譯時(shí),gcc需要很長(zhǎng)的參數(shù)選項(xiàng),在很多情況下,所有的編譯命令使用一組相同的選項(xiàng),如果把這組選項(xiàng)使用一個(gè)變量代表,那么可以把這個(gè)變量放在所有引用編譯器的地方。當(dāng)要改變選項(xiàng)的時(shí)候,只需改變一次這個(gè)變量的內(nèi)容即可。

變量的定義和使用Makefile中的變量是用一個(gè)文本串在Makefile中定義的,這個(gè)文本串就是變量的值。只要在一行的開始寫下這個(gè)變量的名字,后面跟一個(gè)“=”號(hào),以及要設(shè)定這個(gè)變量的值即可定義變量,下面是定義變量的語(yǔ)法:

VARNAME=string使用時(shí),把變量用括號(hào)括起來,并在前面加上$符號(hào),就可以引用變量的值:

${VARNAME}make解釋規(guī)則時(shí),VARNAME在等式右端展開為定義它的字符串。變量一般都在Makefile的頭部定義。按照慣例,所有的Makefile變量都應(yīng)該是大寫。如果變量的值發(fā)生變化,就只需要在一個(gè)地方修改,從而簡(jiǎn)化了Makefile的維護(hù)。Makefile變量舉例現(xiàn)在利用變量把前面的Makefile重寫一遍:OBJS=prog.ocode.oCC=gcc

test:${OBJS} ${CC}–otest${OBJS}

prog.o:prog.c

prog.hcode.h ${CC}–cprog.c–oprog.o

code.o:code.ccode.h ${CC}–ccode.c–ocode.o

clean:

rm–f*.o變量的類型除用戶自定義的變量外,make還允許使用環(huán)境變量使用環(huán)境變量的方法很簡(jiǎn)單,在make啟動(dòng)時(shí),make讀取系統(tǒng)當(dāng)前已定義的環(huán)境變量,并且創(chuàng)建與之同名同值的變量,因此用戶可以像在shell中一樣在Makefile中方便的引用環(huán)境變量。需要注意的是,如果用戶在Makefile中定義了同名的變量,用戶自定義變量將覆蓋同名的環(huán)境變量自動(dòng)變量預(yù)定義變量GNUmake的主要預(yù)定義變量(1/2)$*不包含擴(kuò)展名的目標(biāo)文件名稱。$+所有的依賴文件,以空格分開,并以出現(xiàn)的先后為序,可能包含重復(fù)的依賴文件。$<第一個(gè)依賴文件的名稱。$?所有的依賴文件,以空格分開,這些依賴文件的修改日期比目標(biāo)的創(chuàng)建日期晚。$@目標(biāo)的完整名稱。

$^所有的依賴文件,以空格分開,不包含重復(fù)的依賴文件。$%如果目標(biāo)是歸檔成員,則該變量表示目標(biāo)的歸檔成員名稱。例如,如果目標(biāo)名稱為mytarget.so(image.o),則$@為mytarget.so,而$%為image.o。AR歸檔維護(hù)程序的名稱,默認(rèn)值為ar。ARFLAGS歸檔維護(hù)程序的選項(xiàng)。AS匯編程序的名稱,默認(rèn)值為as。ASFLAGS匯編程序的選項(xiàng)。GNUmake的主要預(yù)定義變量(2/2)CCC編譯器的名稱,默認(rèn)值為cc。CFLAGSC編譯器的選項(xiàng)。CPPC預(yù)編譯器的名稱,默認(rèn)值為$(CC)-E。CPPFLAGSC預(yù)編譯的選項(xiàng)。CXXC++編譯器的名稱,默認(rèn)值為g++。CXXFLAGSC++編譯器的選項(xiàng)。FCFORTRAN編譯器的名稱,默認(rèn)值為f77。FFLAGSFORTRAN編譯器的選項(xiàng)。Makefile的隱含規(guī)則在上面的例子中,幾個(gè)產(chǎn)生目標(biāo)文件的命令都是從“.c”的C語(yǔ)言源文件和相關(guān)文件通過編譯產(chǎn)生“.o”目標(biāo)文件,這也是一般的步驟。實(shí)際上,make可以使工作更加自動(dòng)化,也就是說,make知道一些默認(rèn)的動(dòng)作,它有一些稱作隱含規(guī)則的內(nèi)置的規(guī)則,這些規(guī)則告訴make當(dāng)用戶沒有完整地給出某些命令的時(shí)候,應(yīng)該怎樣執(zhí)行。例如,把生成prog.o和code.o的命令從規(guī)則中刪除,make將會(huì)查找隱含規(guī)則,然后會(huì)找到并執(zhí)行一個(gè)適當(dāng)?shù)拿?。由于這些命令會(huì)使用一些變量,因此可以通過改變這些變量來定制make。象在前面的例子中所定義的那樣,make使用變量CC來定義編譯器,并且傳遞變量CFLAGS(編譯器參數(shù))、CPPFLAGS(C語(yǔ)言預(yù)處理器參數(shù))、TARGET_ARCH(目標(biāo)機(jī)器的結(jié)構(gòu)定義)給編譯器,然后加上參數(shù)-c,后面跟變量$<(第一個(gè)依賴文件名),然后是參數(shù)-o加變量$@(目標(biāo)文件名)。綜上所述,一個(gè)C編譯的具體命令將會(huì)是:

${CC}${CFLAGS}${CPPFLAGS}${TARGET_ARCH}–c$<-o$@隱含規(guī)則舉例在上面的例子中,利用隱含規(guī)則,可以簡(jiǎn)化為:OBJS=prog.ocode.oCC=gcctest:${OBJS} ${CC}–o$@$^

prog.o:prog.c

prog.hcode.hcode.o:code.ccode.h

clean:

rm–f*.omake命令行選項(xiàng)直接在make命令的后面鍵入目標(biāo)名可建立指定的目標(biāo),如果直接運(yùn)行make,則建立第一個(gè)目標(biāo)。還可以用make-fmymakefile

這樣的命令指定make使用特定的makefile,而不是默認(rèn)的GNUmakefile、makefile

或Makefile。GNUmake命令還有一些其他選項(xiàng),下面是GNUmake命令的常用命令行選項(xiàng)命令行選項(xiàng)含義:-CDIR在讀取makefile

之前改變到指定的目錄DIR。-fFILE以指定的FILE文件作為makefile。-h顯示所有的make選項(xiàng)。

-i忽略所有的命令執(zhí)行錯(cuò)誤。-IDIR當(dāng)包含其他makefile

文件時(shí),可利用該選項(xiàng)指定搜索目錄。-n只打印要執(zhí)行的命令,但不執(zhí)行這些命令。-p顯示make變量數(shù)據(jù)庫(kù)和隱含規(guī)則。-s在執(zhí)行命令時(shí)不顯示命令。-w在處理makefile

之前和之后,顯示工作目錄。-WFILE假定文件FILE已經(jīng)被修改。使用automake和autoconf產(chǎn)生Makefile

在開始使用Automake和autoconf之前,請(qǐng)先確認(rèn)系統(tǒng)已經(jīng)安裝以下的軟件:GNUAutomakeGNUAutoconfGNUm4PerlGNULibtool(如果你需要產(chǎn)生sharedlibrary)

Automake

所產(chǎn)生的Makefile

除了可以做到程序的編譯和連接,也已經(jīng)把如何產(chǎn)生程序文件的操作,以及把安裝程序都考慮進(jìn)去了,所以源程序所存放的目錄架構(gòu)最好符合GNU的標(biāo)準(zhǔn)慣例,下面用hello.c

來作為例子進(jìn)行說明。生成一個(gè)源程序在工作目錄下建立一個(gè)新的子目錄devel,再在devel下建立一個(gè)hello的子目錄,這個(gè)目錄將作為存放hello這個(gè)程序及其相關(guān)文件的地方:用編輯器寫個(gè)hello.c文件:#include<stdio.h>int

main(int

argc,char**argv){

printf("Hello,GNU!\n");return0;}使用Autoconf

及Automake

來產(chǎn)生Makefile文件的步驟autoscan

產(chǎn)生一個(gè)configure.in的模板,執(zhí)行autoscan

后會(huì)產(chǎn)生一個(gè)configure.scan的文件,可以用它做為configure.in文件的模板:編輯configure.scan文件,如下所示,并且把文件名改成configure.in執(zhí)行aclocal和autoconf

,分別會(huì)產(chǎn)生aclocal.m4及configure兩個(gè)文件:編輯Makefile.am文件,內(nèi)容如下:執(zhí)行automake--add-missing,Automake

會(huì)根據(jù)Makefile.am產(chǎn)生一些文件,包含最重要的Makefile.in:最后執(zhí)行./configure

GDB實(shí)例練習(xí)vitest.c//編輯文本gcc–gtest.c–otest//GCC編譯gdbtest//GDB開始調(diào)試(gdb)l//GDB查看代碼(gdb)b5//設(shè)置斷點(diǎn)(gdb)infob//查詢斷點(diǎn)情況(gdb)r//運(yùn)行代碼(gdb)pn//查看變量值(gdb)watchn//觀察變量(gdb)n//單步運(yùn)行(gdb)c//程序繼續(xù)運(yùn)行(gdb)clear5//清除斷點(diǎn)最終運(yùn)行結(jié)果(gdb)q//退出GDBGDB調(diào)試錯(cuò)誤程序?qū)嵗齡cc–gwrong_gdb.c–owrong.c程序運(yùn)行g(shù)db

wrong_gdb錯(cuò)誤提示where//查找錯(cuò)誤list查看變量string設(shè)置斷點(diǎn)程序運(yùn)行變量賦值代碼運(yùn)行結(jié)果成功退出GDBMakefile和makeMakefile運(yùn)行結(jié)果使用RCS/CVS來管理源代碼RCS的使用CVS的使用RCS的使用RCS(RevisionControlSystem)即程序改版控制系統(tǒng),主要功能是用來管理文件的版本,可以節(jié)省空間和時(shí)間。這樣就不需要在每個(gè)程序開發(fā)到某一個(gè)階段就將數(shù)據(jù)拷貝到其他的地方備份起來了。RCS提供了如下幾個(gè)最重要的指令的:ci指令:將文件放入RCS目錄下的控制系統(tǒng)co指令:從RCS目錄下將文件取出rcs指令:用來對(duì)RCS文件進(jìn)行參數(shù)的設(shè)置基本操作方式一般而言,RCS所產(chǎn)生出來的文件會(huì)放在RCS目錄中。所以第一步必須要在當(dāng)前的目錄下制作一個(gè)文件:

[root@wyh

linux]#mkdirRCS接下來只要使用ci指令。就可以把文件備份到RCS改版控制系統(tǒng)中:

[root@wyh

linux]#citest.c若要將文件取出,可以使用下列指令:

[root@wyh

linux]#cotest.c取出來的文件是只讀文件,若要取出可以寫入的工作文件,可以加上-l參數(shù)來鎖定它:

[root@wyh

linux]#co-ltest.c

此外將文件放入RCS控制系統(tǒng)時(shí),可以使用-l參數(shù)鎖定文件,那么目錄下的文件依然存在:

[root@wyh

linux]#ci-ltest.c

若要比較當(dāng)前的文件和RCS中最新版本的文件,可以使用下列指令:

[root@wyh

linux]#rcsdiff

test.c指定版本若不指定版本編號(hào)時(shí),co會(huì)從RCS取得最新的版本。如果要以特定的版本號(hào)碼寫入RCS或讀出,可以使用-r參數(shù)選項(xiàng)。[root@wyh

linux]#ci-l-r3.25test.c<--以3.25作為版本編號(hào)[root@wyh

linux]#co-l-r1.2test.c <--將RCS中1.2版的test.c讀出

此外,rcsdiff也可以用來指定任何一個(gè)版本和當(dāng)前程序代碼進(jìn)行比較。[root@wyh

linux]#rcsdiff-r3.25test.c <--取出3.25版與test.c進(jìn)行比較

關(guān)鍵詞的使用在RCS中可以將關(guān)鍵詞變量放入程序代碼中。這些變量經(jīng)過RCS會(huì)變成版本的注解。用戶可以將這些關(guān)鍵詞說明當(dāng)作是程序中的批注。常用的關(guān)鍵詞如下:$Author$:將版本放入RCS的用戶名稱。$Data$:記錄程序代碼放入RCS時(shí)的日期和時(shí)間。$Header$:記錄文件的標(biāo)頭,包括RCS路徑名稱、版本號(hào)碼、日期、作者等。$ID$:和$Header$相同,但不包括RCS路徑名稱。$Locker$:記錄鎖定本版本的用戶名稱。$Log$:記錄將RCS鎖住的時(shí)間,所輸入的文本語(yǔ)句。$RCSfiles$:記錄RCS文件名稱。$Rivision$:指定版本號(hào)碼。$Source$:RCS文件名稱,包括其路徑。$State$:使用-s選項(xiàng)所指定的特殊狀態(tài)使用關(guān)鍵詞的步驟在程序代碼中加入任一關(guān)鍵詞

[root@wyh

linux]#vitest.c將程序代碼放入RCS版本控制系統(tǒng)

[root@wyh

linux]#ci-ltest.c將文件再次取出。在取出的過程中,co會(huì)將每個(gè)關(guān)鍵詞展開成其對(duì)應(yīng)的值

[root@wyh

linux]#co-ltest.c [root@wyh

linux]#cattest.c

CVS的使用CVS(ConcurrentVersionSystem)是個(gè)版本控制系統(tǒng),利用該系統(tǒng)可以記錄源代碼文件的歷史。例如,當(dāng)軟件修改時(shí)會(huì)產(chǎn)生Bug,并且可能在做這次修改后很長(zhǎng)時(shí)間不會(huì)發(fā)現(xiàn)這些問題。使用CVS就可以容易地回顧老的代碼版本去發(fā)現(xiàn)哪一次的修改導(dǎo)致這些問題。如果CVS保留每一次的代碼版本,會(huì)浪費(fèi)很多的空間。因此CVS使用一種比較聰明的辦法保存多個(gè)版本在一個(gè)文件中。它僅僅保留版本間的不同內(nèi)容。如果很多人在同一個(gè)項(xiàng)目上工作,則CVS使用讓不同開發(fā)者獨(dú)立工作的方式解決了這個(gè)問題。每一個(gè)開發(fā)者的工作都在他自己的目錄內(nèi),并且CVS將在每個(gè)開發(fā)者的工作完成后進(jìn)行合并工作。在Linux下,CVS的使用一般是以命令行方式。通常,CVS有兩種使用方式,一是本機(jī)方式,一是遠(yuǎn)程執(zhí)行方式。CVS的命令格式是:

cvs[cvs的選項(xiàng)]cvs的動(dòng)作[選項(xiàng)]可以用cvs–Hcommand列出命令command的使用方法開始項(xiàng)目用CVS管理代碼,首先要?jiǎng)?chuàng)建一個(gè)“信息倉(cāng)庫(kù)”。“信息倉(cāng)庫(kù)”簡(jiǎn)單來說包含一個(gè)目錄結(jié)構(gòu)。它包括要管理的源代碼和用于管理源代碼的各種管理文件先設(shè)置環(huán)境變量CVSROOT,指向信息倉(cāng)庫(kù)的絕對(duì)路徑,然后調(diào)用CVS的init命令:#CVSROOT=/usr/local/cvsroot;exportCVSROOT#cvsinit#ls-l$CVSROOT添加項(xiàng)目的文件、目錄到信息倉(cāng)庫(kù)

要將需要管理的項(xiàng)目的文件加入到信息倉(cāng)庫(kù),并做上標(biāo)志。如果從頭開始一個(gè)新的項(xiàng)目,就需要?jiǎng)?chuàng)建一個(gè)單獨(dú)的目錄,并把所有要使用的文件做一個(gè)有效的組織。而如果在開始使用源文件之前該目錄就已經(jīng)存在,則只需進(jìn)入該目錄就行了。然后,就可以輸入源文件目錄:

#cvsimport-m"CreateSourceDir"

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(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)論