c語言程序設計現(xiàn)代方法15-編寫大型程序_第1頁
c語言程序設計現(xiàn)代方法15-編寫大型程序_第2頁
c語言程序設計現(xiàn)代方法15-編寫大型程序_第3頁
c語言程序設計現(xiàn)代方法15-編寫大型程序_第4頁
c語言程序設計現(xiàn)代方法15-編寫大型程序_第5頁
已閱讀5頁,還剩91頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

Copyright?2008W.W.Norton&Company.Allrightsreserved.1第15章編寫大型程序源文件一個C程序可以分成任意數(shù)量的源文件。依照慣例,源文件具有.c擴展名。每一個源文件包含部分程序,主要是函數(shù)和變量的定義。必須有一個源文件包含一個名為

main

的函數(shù),它是程序的入口。Copyright?2008W.W.Norton&Company.Allrightsreserved.2源文件考慮編寫一個簡單的計算器程序。程序可以求按波蘭式輸入的整型表達式的值。在逆波蘭式中,操作符在操作數(shù)之后。如果用戶輸入這樣一個表達式 305-7*

程序應該打印出它的值(在本例中是175).Copyright?2008W.W.Norton&Company.Allrightsreserved.3源文件程序逐個讀入操作數(shù)和操作符,并使用一個棧去追蹤中間結(jié)果。如果程序讀入一個數(shù),它就把這個數(shù)壓入棧。如果程序讀入一個操作符,它就從棧里彈出兩個數(shù),并執(zhí)行該運算,然后將計算結(jié)果壓入棧。當程序到達用戶輸入的末尾時,表達式的值就已經(jīng)在棧中。Copyright?2008W.W.Norton&Company.Allrightsreserved.4源文件怎樣計算表達式

30

5

-

7

*將30壓入棧.將5壓入棧.彈出棧頂?shù)膬蓚€數(shù),將30減去5,得25,然后將結(jié)果壓回棧.將7壓入棧.彈出棧頂?shù)膬蓚€數(shù),將它們相乘,再把結(jié)果壓回棧.現(xiàn)在棧里就是175,即表達式的值.Copyright?2008W.W.Norton&Company.Allrightsreserved.5源文件程序的主函數(shù)將包含一個執(zhí)行如下操作的循環(huán):讀入一個符號(數(shù)或運算符).如果它是數(shù),就把它壓入棧.如果是運算符,就從棧里彈出操作數(shù),并執(zhí)行該運算,然后把結(jié)果壓入棧.當把這樣一個程序分成文件時,把相關(guān)的函數(shù)和變量放在同一個文件中是有意義的.Copyright?2008W.W.Norton&Company.Allrightsreserved.6源文件讀入符號的函數(shù)和相關(guān)的處理符號的函數(shù)一起,可以形成一個源文件(設為token.c).與棧相關(guān)的函數(shù),如

push,pop,make_empty,is_empty,和is_full

可以放入一個不同的文件

stack.c.表示棧的變量也應該放入

stack.c.主函數(shù)放入另外一個文件

calc.c.Copyright?2008W.W.Norton&Company.Allrightsreserved.7源文件將一個程序分為多個源文件有重大好處:把相關(guān)的函數(shù)和變量放入一個文件中有助于澄清程序的結(jié)構(gòu).每個源文件可以單獨編譯,節(jié)省時間.函數(shù)能夠更容易地用于其他程序中.Copyright?2008W.W.Norton&Company.Allrightsreserved.8頭文件當一個程序分為幾個源文件時,會有如下問題:一個文件中的函數(shù)怎樣調(diào)用在另一個文件中定義的函數(shù)?函數(shù)怎樣訪問其他文件中的外部變量?兩個文件怎樣共享相同的宏定義和類型定義?答案在于

#include,它使多個文件共享信息成為可能.Copyright?2008W.W.Norton&Company.Allrightsreserved.9頭文件#include

告訴預處理器把指定的文件內(nèi)容插入進來.多個文件需要共享的信息可以放入這樣一個文件中.然后用#include

就可以把這個文件的內(nèi)容包含到每一個源文件中.這樣被包含進來的文件就叫頭文件(有時也叫包含文件).依照慣例,頭文件具有.h擴展名.Copyright?2008W.W.Norton&Company.Allrightsreserved.10#include

#include

有兩種主要形式.一種用于C庫中的頭文件: #include<filename>另一種用于所有的其他頭文件: #include"filename"兩者的不同之處在于編譯器如何確定頭文件的位置.Copyright?2008W.W.Norton&Company.Allrightsreserved.11#include定位頭文件的典型規(guī)則:#include

<filename>:查找系統(tǒng)頭文件所在的路徑.#include

“filename”:查找當前路徑,然后查找系統(tǒng)頭文件所在的路徑.查找頭文件的位置可以改變,通常通過命令行選項,例如

-Ipath.Copyright?2008W.W.Norton&Company.Allrightsreserved.12#include不要用尖括號包含你寫的文件: #include<myheader.h>/***WRONG***/預處理器可能會在系統(tǒng)頭文件所在的地方尋找

myheader.hCopyright?2008W.W.Norton&Company.Allrightsreserved.13#include#include指示中的文件名可以包含盤符和路徑: #include"c:\cprogs\utils.h" /*Windowspath*/ #include"/cprogs/utils.h" /*UNIXpath*/雖然

#include

中的引號使文件名看起來像字符串,但是預處理器不會那樣處理.Copyright?2008W.W.Norton&Company.Allrightsreserved.14#include通常最好不要在#include包含盤符或路徑.Windows上#include的壞例子: #include"d:utils.h" #include"\cprogs\include\utils.h" #include"d:\cprogs\include\utils.h"好版本: #include"utils.h" #include"..\include\utils.h"Copyright?2008W.W.Norton&Company.Allrightsreserved.15#include#include

還有第三種形式: #includetokens

tokens

是任意的預處理符號序列.預處理器將掃描符號并用找到的宏替換它.在宏替換之后的#include必須滿足合法形式.第三種

#include

形式的好處是文件名可以用宏定義,而不必硬編碼.Copyright?2008W.W.Norton&Company.Allrightsreserved.16#include例: #ifdefined(IA32) #defineCPU_FILE"ia32.h" #elifdefined(IA64) #defineCPU_FILE"ia64.h" #elifdefined(AMD64) #defineCPU_FILE"amd64.h" #endif

#includeCPU_FILECopyright?2008W.W.Norton&Company.Allrightsreserved.17共享宏定義和類型定義大多數(shù)大型程序擁有多個源文件共享的宏定義和類型定義.這些定義應該放入頭文件中.Copyright?2008W.W.Norton&Company.Allrightsreserved.18共享宏定義和類型定義設程序使用宏

BOOL,TRUE,和FALSE.它們的定義可以放入一個頭文件boolean.h中: #defineBOOLint

#defineTRUE1 #defineFALSE0任何需要這些宏的源文件可以簡單地使用 #include"boolean.h"Copyright?2008W.W.Norton&Company.Allrightsreserved.19共享宏定義和類型定義一個程序的兩個文件包含

boolean.h:Copyright?2008W.W.Norton&Company.Allrightsreserved.20共享宏定義和類型定義在頭文件中,類型定義也很普遍.例如,我們可以使用

typedef

創(chuàng)建一個Bool

類型,取代BOOL宏.如果這樣,boolean.h

文件就有如下形式: #defineTRUE1 #defineFALSE0

typedefintBool;Copyright?2008W.W.Norton&Company.Allrightsreserved.21共享宏定義和類型定義把宏和類型的定義放入頭文件的好處:節(jié)省時間.我們不必拷貝宏到需要的地方.使程序容易修改.要改變宏或類型定義,只需要修改頭文件.避免了因包含同樣一個宏或類型的不同定義而導致的不一致性.Copyright?2008W.W.Norton&Company.Allrightsreserved.22共享函數(shù)原型假設一個源文件含有對函數(shù)

f

的調(diào)用,而f

定義在另一個文件

foo.c中.不加聲明地調(diào)用

f

是危險的.編譯器認為

f的返回類型是

int.它也認為參數(shù)的個數(shù)與調(diào)用f

時的自變量的個數(shù)一致.自變量被自動地轉(zhuǎn)為默認的類型.Copyright?2008W.W.Norton&Company.Allrightsreserved.23共享函數(shù)原型在調(diào)用f的文件中聲明f可以解決上述問題,但是又會帶來維護的噩夢.一個好的解決辦法是把

f的原型放入一個頭文件(foo.h),再把頭文件包含進需要調(diào)用

f

的文件中.我們也需要在foo.c

中包含foo.h

,使編譯器可以檢查

foo.h中的f原型與foo.c中的定義是否一致.Copyright?2008W.W.Norton&Company.Allrightsreserved.24共享函數(shù)原型如果

foo.c

含有其他的函數(shù),大多可以在

foo.h中聲明.然而只打算在

foo.c

中使用的函數(shù)不應該聲明在頭文件中.Copyright?2008W.W.Norton&Company.Allrightsreserved.25共享函數(shù)原型可以用逆波蘭式表達式計算器的例子顯示頭文件中函數(shù)原型的使用.stack.c

文件中含有

make_empty,is_empty,is_full,push,和

pop

函數(shù)的定義.這些函數(shù)的原型應該放入stack.h

頭文件中: voidmake_empty(void);

intis_empty(void);

intis_full(void); voidpush(inti);

intpop(void);Copyright?2008W.W.Norton&Company.Allrightsreserved.26共享函數(shù)原型我們把

stack.h

包含在

calc.c

中,允許編譯器檢查棧函數(shù)調(diào)用.我們也把

stack.h

包含在

stack.c

中,使編譯器能夠檢驗stack.h中的函數(shù)原型與stack.c中的定義相匹配.Copyright?2008W.W.Norton&Company.Allrightsreserved.27共享函數(shù)原型Copyright?2008W.W.Norton&Company.Allrightsreserved.28共享函數(shù)原型要在多個文件中共享一個函數(shù),我們把它的定義放在一個源文件中,然后把聲明放在需要調(diào)用它的文件中.共享外部變量也采用同樣的方式.Copyright?2008W.W.Norton&Company.Allrightsreserved.29共享變量聲明一個聲明和定義變量

i

的例子(使編譯器留出空間):

inti;關(guān)鍵字

extern

用于聲明變量(而不是定義它): externinti;extern

告訴編譯器

i

是在程序的其他地方定義的,因此不必為它分配空間.Copyright?2008W.W.Norton&Company.Allrightsreserved.30共享變量聲明當我們使用

extern

聲明數(shù)組時,我們可以省略數(shù)組的長度: externinta[];因為此時編譯器不為

a

分配空間,所以不必知道

a的長度.Copyright?2008W.W.Norton&Company.Allrightsreserved.31共享變量聲明要在幾個源文件中共享變量

i,我們首先在一個文件里定義

i:

inti;如果

i

需要被初始化,那么初始化應該放在這個文件里.其他文件將含有對

i的聲明: externinti;通過在每個文件里聲明

i,就可以在其他文件里訪問或修改

i.Copyright?2008W.W.Norton&Company.Allrightsreserved.32共享變量聲明當同一個變量的聲明出現(xiàn)在不同的文件里時,編譯器不能檢查變量的聲明與定義是否一致.例如,一個文件有定義

inti;

另一個文件有聲明 externlongi;這種錯誤會導致程序不可預知的行為表現(xiàn).Copyright?2008W.W.Norton&Company.Allrightsreserved.33共享變量聲明為了避免不一致,共享變量的聲明通常放在頭文件里.一個需要訪問特定變量的源文件可以將適當?shù)念^文件包含進來.此外,每一個含有變量聲明的頭文件也被包含進定義變量的源文件中,使得編譯器能夠檢查二者是否匹配.Copyright?2008W.W.Norton&Company.Allrightsreserved.34嵌套包含一個頭文件可以含有

#include.stack.h

含有下面的原型:

intis_empty(void);

intis_full(void);既然這些函數(shù)只返回0或1,把它們的返回類型聲明為

Bool類型是一個好主意:

Boolis_empty(void);

Boolis_full(void);我們需要在stack.h

中包含

boolean.h,使得

當stack.h被編譯時

Bool類型生效.Copyright?2008W.W.Norton&Company.Allrightsreserved.35嵌套包含傳統(tǒng)上,C程序員避開嵌套包含.然而,反對嵌套包含的偏見已經(jīng)很大程度地淡化,部分因為嵌套包含在C++中很普遍.Copyright?2008W.W.Norton&Company.Allrightsreserved.36保護頭文件如果一個源文件包含同一個頭文件兩次,就會導致編譯錯誤.當頭文件包含其他的頭文件時,這個問題很普遍.假設

file1.h

包含file3.h,file2.h

包含file3.h,prog.c

包含

file1.h

file2.h.Copyright?2008W.W.Norton&Company.Allrightsreserved.37保護頭文件當

prog.c

被編譯時,file3.h

將被編譯兩次.Copyright?2008W.W.Norton&Company.Allrightsreserved.38保護頭文件包含同一個頭文件兩次并非總導致編譯錯誤.如果文件只含有宏定義,函數(shù)原型和變量聲明,不會有困難.然而如果文件含有類型定義,就會出現(xiàn)編譯錯誤.Copyright?2008W.W.Norton&Company.Allrightsreserved.39保護頭文件為安全起見,一個好的做法是保護所有的頭文件,避免重復包含.這樣,我們可以增加類型定義而不會有忘記保護文件的風險.此外,我們很可能節(jié)省了時間,因為避免了不必要的重復編譯.Copyright?2008W.W.Norton&Company.Allrightsreserved.40保護頭文件為了保護頭文件,我們把文件內(nèi)容放入

#ifndef-#endif

中.怎樣保護

boolean.h

文件: #ifndefBOOLEAN_H #defineBOOLEAN_H #defineTRUE1 #defineFALSE0

typedefintBool; #endifCopyright?2008W.W.Norton&Company.Allrightsreserved.41保護頭文件選取與頭文件名相似的宏名是避免與其他宏沖突的好辦法.既然我們不能為宏取名

BOOLEAN.H,像

BOOLEAN_H

這樣的名字是個好的選擇.Copyright?2008W.W.Norton&Company.Allrightsreserved.42頭文件中的#error

指示#error

經(jīng)常被放在頭文件里用于檢查頭文件不應被包含的條件.假設一個頭文件使用了C89標準之前沒有的特征.

#ifndef

檢驗__STDC__

宏是否存在: #ifndef__STDC__ #error

This

header

requires

a

Standard

C

compiler #endifCopyright?2008W.W.Norton&Company.Allrightsreserved.43把程序分為文件設計程序涉及到確定需要哪些函數(shù),并把這些函數(shù)組織成邏輯相關(guān)的組.一旦程序設計好,有個簡單的辦法把程序分成文件.Copyright?2008W.W.Norton&Company.Allrightsreserved.44把程序分為文件每個函數(shù)集形成一個單獨的源文件(foo.c).每個源文件有個相應的頭文件(foo.h).foo.h

含有定義在foo.c中的函數(shù)的原型.只在

foo.c

中使用的函數(shù)不應該在

foo.h中聲明.如果一個源文件要調(diào)用foo.c定義的函數(shù),就需要把foo.h包含進來.foo.h

也應該被包含到

foo.c

中,使得編譯器能夠檢驗foo.h中的原型與foo.c中的定義相匹配.Copyright?2008W.W.Norton&Company.Allrightsreserved.45把程序分為文件主函數(shù)放在一個文件中,文件名與程序名相配.主函數(shù)所在的文件也可能含有其他函數(shù),只要它們不被其他文件調(diào)用.Copyright?2008W.W.Norton&Company.Allrightsreserved.46程序設計:文本格式化我們把這項技術(shù)應用于一個名為justify

的文本格式化程序.假設文件

quote

含有如下輸入:Cisquirky,flawed,andanenormoussuccess.Althoughaccidentsofhistorysurelyhelped,itevidentlysatisfiedaneed

forasystemimplementationlanguageefficientenoughtodisplaceassemblylanguage,yetsufficientlyabstractandfluenttodescribealgorithmsandinteractionsinawidevarietyofenvironments.--DennisM.RitchieCopyright?2008W.W.Norton&Company.Allrightsreserved.47程序設計:文本格式化在UNIX或Windows命令行運行程序,我們輸入如下命令 justify<quote<

符號告訴操作系統(tǒng)justify將從文件

quote

而不是從鍵盤接收輸入.這個特性叫做輸入重定向,UNIX,Windows和其他一些操作系統(tǒng)都支持.Copyright?2008W.W.Norton&Company.Allrightsreserved.48程序設計:文本格式化Justify的輸出:Cisquirky,flawed,andanenormoussuccess.Althoughaccidentsofhistorysurelyhelped,itevidentlysatisfiedaneedforasystemimplementationlanguageefficientenoughtodisplaceassemblylanguage,yetsufficientlyabstractandfluenttodescribealgorithmsandinteractionsinawidevarietyofenvironments.--DennisM.Ritchiejustify

的輸出正常顯示在屏幕上,我們可以通過使用輸出重定向把它存儲到一個文件中: justify<quote>newquoteCopyright?2008W.W.Norton&Company.Allrightsreserved.49程序設計:文本格式化justify

將刪除多余的空白和空行,也能填充和調(diào)整行.填充一行是指添加單詞直到再多一詞就會行溢出.調(diào)整一行是指在單詞間添加額外的空白,使得每行具有同樣的長度(60字符).調(diào)整必須要做,才能使一行中單詞間的距離相等或接近相等.輸出的最后一行不用調(diào)整.Copyright?2008W.W.Norton&Company.Allrightsreserved.50程序設計:文本格式化我們假設沒有長度超過20字符的單詞,包括鄰近的任何標點符號.如果程序遇到長單詞,它必須忽略掉20個字符之后的所有字符并用一個星號代替.例如,單詞 antidisestablishmentarianism

會被打印成

antidisestablishment*Copyright?2008W.W.Norton&Company.Allrightsreserved.51程序設計:文本格式化程序不能像它讀單詞那樣一個一個地寫出來.必須把它們存儲到一個行緩沖區(qū),直到足夠填滿一行.Copyright?2008W.W.Norton&Company.Allrightsreserved.52程序設計:文本格式化程序的核心是一個循環(huán): for(;;){

readword; if(can’treadword){

writecontentsoflinebufferwithoutjustification;

terminateprogram; } if(worddoesn’tfitinlinebuffer){

writecontentsoflinebufferwithjustification;

clearlinebuffer; }

addwordtolinebuffer; }Copyright?2008W.W.Norton&Company.Allrightsreserved.53程序設計:文本格式化程序分成三個源文件:word.c:與單詞相關(guān)的函數(shù)line.c:與行緩沖區(qū)相關(guān)的函數(shù)justify.c:包含主函數(shù)我們也需要兩個頭文件:word.h:定義在word.c中的函數(shù)的原型line.h:定義在line.c中的函數(shù)的原型word.h

包含一個讀單詞的函數(shù)的原型.Copyright?2008W.W.Norton&Company.Allrightsreserved.54word.h#ifndefWORD_H#defineWORD_H

/***********************************************************read_word:Readsthenextwordfromtheinputand**storesitinword.Makeswordemptyifno**wordcouldbereadbecauseofend-of-file.**Truncatesthewordifitslengthexceeds**len.***********************************************************/voidread_word(char*word,intlen);

#endifCopyright?2008W.W.Norton&Company.Allrightsreserved.55程序設計:文本格式化主循環(huán)顯示需要如下一些函數(shù):寫行緩沖區(qū)的內(nèi)容,不帶調(diào)整確定行緩沖區(qū)還有多少字符寫帶調(diào)整的行緩沖區(qū)的內(nèi)容,清空緩沖區(qū)向行緩沖區(qū)添加單詞我們稱這些函數(shù)為

flush_line,space_remaining,write_line,clear_line,和add_word.Copyright?2008W.W.Norton&Company.Allrightsreserved.56line.h#ifndefLINE_H#defineLINE_H

/***********************************************************clear_line:Clearsthecurrentline.***********************************************************/voidclear_line(void);

/***********************************************************add_word:Addswordtotheendofthecurrentline.**Ifthisisnotthefirstwordontheline,**putsonespacebeforeword.***********************************************************/voidadd_word(constchar*word);Copyright?2008W.W.Norton&Company.Allrightsreserved.57/***********************************************************space_remaining:Returnsthenumberofcharactersleft**inthecurrentline.***********************************************************/intspace_remaining(void);

/***********************************************************write_line:Writesthecurrentlinewith**justification.***********************************************************/voidwrite_line(void);

/***********************************************************flush_line:Writesthecurrentlinewithout**justification.Ifthelineisempty,does**nothing.***********************************************************/voidflush_line(void);

#endifCopyright?2008W.W.Norton&Company.Allrightsreserved.58程序設計:文本格式化在我們寫

word.c和line.c文件之前,我們可以使用在

word.h

和line.h

中聲明的函數(shù)編寫justify.c,即主程序.編寫這個文件主要是把最初的循環(huán)設計翻譯成C.Copyright?2008W.W.Norton&Company.Allrightsreserved.59justify.c/*Formatsafileoftext*/

#include<string.h>#include"line.h"#include"word.h"

#defineMAX_WORD_LEN20

intmain(void){charword[MAX_WORD_LEN+2];

intword_len;

Copyright?2008W.W.Norton&Company.Allrightsreserved.60

clear_line();for(;;){read_word(word,MAX_WORD_LEN+1);word_len=strlen(word);if(word_len==0){flush_line();return0;}if(word_len>MAX_WORD_LEN)word[MAX_WORD_LEN]='*';if(word_len+1>space_remaining()){write_line();clear_line();}add_word(word);}}

Copyright?2008W.W.Norton&Company.Allrightsreserved.61程序設計:文本格式化main

使用如下技巧處理超過20個字符的單詞.當調(diào)用

read_word時,main

告訴它截去超過21個字符的單詞.在

read_word

返回后,main

檢驗

word

是否含有超過20個字符的串.如果是,那么word必然至少21個字符(被截前),main

把第21個字符換成星號.Copyright?2008W.W.Norton&Company.Allrightsreserved.62程序設計:文本格式化word.h

頭文件只有一個函數(shù)原型,read_word.read_word

容易編寫,如果我們加一個小的幫助函數(shù)

read_char.read_char的任務是讀一個單獨的字符,如果是一個換行符或制表符,就轉(zhuǎn)成空格.讓read_word

調(diào)用

read_char(取代getchar)解決把換行符和制表符看成空格的問題.Copyright?2008W.W.Norton&Company.Allrightsreserved.63word.c#include<stdio.h>#include"word.h"

intread_char(void){

intch=getchar();

if(ch=='\n'||ch=='\t')return'';returnch;}Copyright?2008W.W.Norton&Company.Allrightsreserved.64voidread_word(char*word,intlen){

intch,pos=0;

while((ch=read_char())=='');while(ch!=''&&ch!=EOF){if(pos<len)word[pos++]=ch;

ch=read_char();}word[pos]='\0';}Copyright?2008W.W.Norton&Company.Allrightsreserved.65程序設計:文本格式化line.c

提供聲明在line.h中的函數(shù)的定義.line.c

也需要跟蹤行緩沖區(qū)狀態(tài)的變量:line:當前行的字符line_len:當前行的字符數(shù)num_words:當前行的單詞數(shù)Copyright?2008W.W.Norton&Company.Allrightsreserved.66line.c#include<stdio.h>#include<string.h>#include"line.h"#defineMAX_LINE_LEN60

charline[MAX_LINE_LEN+1];intline_len=0;intnum_words=0;

voidclear_line(void){line[0]='\0';line_len=0;num_words=0;}Copyright?2008W.W.Norton&Company.Allrightsreserved.67voidadd_word(constchar*word){if(num_words>0){line[line_len]='';line[line_len+1]='\0';line_len++;}

strcat(line,word);line_len+=strlen(word);num_words++;}

intspace_remaining(void){returnMAX_LINE_LEN-line_len;}Copyright?2008W.W.Norton&Company.Allrightsreserved.68voidwrite_line(void){

intextra_spaces,spaces_to_insert,i,j;

extra_spaces=MAX_LINE_LEN-line_len;for(i=0;i<line_len;i++){if(line[i]!='')

putchar(line[i]);else{spaces_to_insert=extra_spaces/(num_words-1);for(j=1;j<=spaces_to_insert+1;j++)

putchar('');extra_spaces-=spaces_to_insert;num_words--;}}

putchar('\n');}

voidflush_line(void){if(line_len>0)puts(line);}Copyright?2008W.W.Norton&Company.Allrightsreserved.69建造多文件程序建造大型程序與生成小型程序需要同樣的基本步驟:編譯連接Copyright?2008W.W.Norton&Company.Allrightsreserved.70建造多文件程序每個源文件必須分別編譯.頭文件不必編譯.當源文件被編譯時,它所包含的頭文件被自動編譯.對于每一個源文件,編譯器產(chǎn)生一個含有目標代碼的文件.這些文件—稱為目標文件—在Unix上具有.o擴展名,在Windows上具有

.obj擴展名.Copyright?2008W.W.Norton&Company.Allrightsreserved.71建造多文件程序連接器合并這些目標文件與庫函數(shù)代碼,生成可執(zhí)行文件.連接器還負責解析外部引用.當一個文件中的函數(shù)調(diào)用另一個文件中的函數(shù)或訪問另一個文件中的變量時,就發(fā)生外部引用.Copyright?2008W.W.Norton&Company.Allrightsreserved.72建造多文件程序大多數(shù)的編譯器允許一步生成程序.生成justify的GCC命令:

gcc-ojustifyjustify.cline.cword.c三個源文件首先編譯成目標代碼.目標文件自動地被送往連接器并被合并成一個單獨的文件.-o

選項指明可執(zhí)行文件命名為

justify.Copyright?2008W.W.Norton&Company.Allrightsreserved.73生成文件(Makefiles)為了使生成大型程序更簡單,UNIX引入生成文件(makefile)的概念.

一個生成文件不但列出屬于程序的文件,而且描述文件間的依賴關(guān)系.假設文件

foo.c

包含文件

bar.h,我們稱foo.c

依賴

bar.h,因為

bar.h的改變將要求我們重新編譯foo.c.Copyright?2008W.W.Norton&Company.Allrightsreserved.74生成文件(Makefiles)justify

程序的UNIX生成文件:justify:justify.oword.oline.o

gcc-ojustifyjustify.oword.oline.o

justify.o:justify.cword.hline.h

gcc-cjustify.c

word.o:word.cword.h

gcc-cword.c

line.o:line.cline.h

gcc-cline.cCopyright?2008W.W.Norton&Company.Allrightsreserved.75生成文件(Makefiles)這些行分成四組;每組是一個規(guī)則.每個規(guī)則的第一行給出目標文件,后面的文件是它所依賴的.第二行是一個命令,當目標因為它所依賴的文件發(fā)生變化而應該重新生成時,這個命令將被執(zhí)行.Copyright?2008W.W.Norton&Company.Allrightsreserved.76生成文件(Makefiles)在第一個規(guī)則里,justify(可執(zhí)行文件)是目標: justify:justify.oword.oline.o

gcc

-o

justify

justify.o

word.o

line.o第一行表明

justify

依賴文件justify.o,word.o,和line.o.在程序建造之后,如果任一個文件發(fā)生變化,justify

需要重新建造.第二行的命令表明如何再建造.Copyright?2008W.W.Norton&Company.Allrightsreserved.77生成文件(Makefiles)在第二個規(guī)則里,justify.o

是目標: justify.o:justify.cword.hline.h

gcc-cjustify.c第一行表明,如果justify.c,word.h,或

line.h

發(fā)生變化,

justify.o

需要再造.下面一行顯示如何更新

justify.o(通過重新編譯justify.c).-c

選項告訴編譯器編譯justify.c,但不連接.Copyright?2008W.W.Norton&Company.Allrightsreserved.78生成文件(Makefiles)一旦我們?yōu)橐粋€程序創(chuàng)建了生成文件,我們就能使用

make

建造程序.通過檢查每個文件的時間和日期,make

能夠確定哪些文件已經(jīng)過時.于是調(diào)用必要的命令再造程序.Copyright?2008W.W.Norton&Company.Allrightsreserved.79生成文件(Makefiles)生成文件中的每一個命令的前面必須是一個制表符,不是一系列空格.生成文件通常名為Makefile(或makefile).當使用

make

時,它自動地在當前路徑中檢查具有這樣的名字的文件.Copyright?2008W.W.Norton&Company.Allrightsreserved.80生成文件(Makefiles)要調(diào)用

make,使用命令 make

target

其中

target

是生成文件中的一個目標.如果調(diào)用make時沒有指定目標,它將建造第一個規(guī)則的目標.除了第一個規(guī)則的這個特性,生成文件里的其他規(guī)則的順序是任意的.Copyright?2008W.W.Norton&Company.Allrightsreserved.81生成文件(Makefiles)真實得生成文件并非總是容易理解.有許多技術(shù)可以減少生成文件的冗余并使它們?nèi)菀仔薷?這些技術(shù)大大降低了生成文件的可讀性.生成文件的替代物包括某些集成開發(fā)環(huán)境支持的工程文件.Copyright?2008W.W.Norton&Company.Allrightsreserved.82連接中的錯誤有些錯誤在編譯時不能被發(fā)現(xiàn),而在連接時被發(fā)現(xiàn).如果一個函數(shù)或變量的定義丟失,連接器將無法解析對它的外部引用結(jié)果是這樣一個消息

“undefinedsymbol”

或“undefinedreference.”Copyright?2008W.W.Norton&Company.Allrightsreserved.83連接中的錯誤連接錯誤的普遍原因:拼寫錯誤.如果一個變量或函數(shù)名拼寫錯,連接器將報告丟失.文件丟失.

如果連接器找不到foo.c中的函數(shù),它也許不知道這個文件.庫丟失.連接器可能不能找到程序中使用的所有的庫函數(shù).在UNIX中,當使用<math.h>的程序被連接時,需要指定

-lm

選項.Copyright?2008W.W.Norton&Company.Allrightsreserved.84重建程序在程序開發(fā)中,我們很少需要編譯所有的文件.為了節(jié)省時間,再造過程應該只編譯那些被最近的修改所影響的文件.假設一個程序被設計為每個源文件帶一個頭文件.在修改之后,要看有多少文件需要重新編譯,我們需要考慮兩種可能.Copyright?2008W.W.Norton&Company.Allrightsreserved.85重建程序如果變化影響了一個單獨的源文件,只有那個文件必須被重新編譯.假設我們決定精簡word.c中的read_char

函數(shù):

intread_char(void) {

intch=getchar();

return

(ch

==

'\n'

||

ch==

'\t')

?

''

:

ch; }這個修改不影響

word.h,因此我們只需重新編譯

word.c并再連接程序.Copyright?2008W.W.Norton&Company.Allrightsreserved.86重建程序第二種可能是變化影響了頭文件.對于這種情形,我們應該重新編譯所有的包含該頭文件的文件,因為它們可能受到潛在的影響.Copyright?2008W.W.Norton&Company.Allrightsreserved.87重建程序假設我們修改

read_word,使得它返回所讀單詞的長度.首先,我們改變word.h

中的read_word的原型

:/***********************************************************read_word:Readsthenextwordfromtheinputand**

溫馨提示

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

最新文檔

評論

0/150

提交評論