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ù)免費閱讀

下載本文檔

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

文檔簡介

1、第15章: 編寫大型程序Copyright 2008 W. W. Norton & Company.All rights reserved.1第 15 章編寫大型程序編寫大型程序第15章: 編寫大型程序源文件 一個 C 程序可以分成任意數(shù)量的源文件。 依照慣例, 源文件具有.c擴展名。 每一個源文件包含部分程序,主要是函數(shù)和變量的定義。 必須有一個源文件包含一個名為 main 的函數(shù),它是程序的入口。Copyright 2008 W. W. Norton & Company.All rights reserved.2第15章: 編寫大型程序源文件 考慮編寫一個簡單的計算器程序。

2、 程序可以求按波蘭式輸入的整型表達式的值。在逆波蘭式中,操作符在操作數(shù)之后。 如果用戶輸入這樣一個表達式30 5 - 7 *程序應該打印出它的值 (在本例中是175).Copyright 2008 W. W. Norton & Company.All rights reserved.3第15章: 編寫大型程序源文件 程序逐個讀入操作數(shù)和操作符,并使用一個棧去追蹤中間結果。 如果程序讀入一個數(shù),它就把這個數(shù)壓入棧。 如果程序讀入一個操作符,它就從棧里彈出兩個數(shù),并執(zhí)行該運算,然后將計算結果壓入棧。 當程序到達用戶輸入的末尾時,表達式的值就已經(jīng)在棧中。Copyright 2008 W. W

3、. Norton & Company.All rights reserved.4第15章: 編寫大型程序源文件 怎樣計算表達式 30 5 - 7 *1.將 30 壓入棧.2.將 5 壓入棧.3.彈出棧頂?shù)膬蓚€數(shù), 將30減去5, 得25, 然后將結果壓回棧.4.將 7 壓入棧.5.彈出棧頂?shù)膬蓚€數(shù), 將它們相乘, 再把結果壓回棧. 現(xiàn)在棧里就是 175, 即表達式的值.Copyright 2008 W. W. Norton & Company.All rights reserved.5第15章: 編寫大型程序源文件 程序的主函數(shù)將包含一個執(zhí)行如下操作的循環(huán): 讀入一個符號 (數(shù)

4、或運算符). 如果它是數(shù), 就把它壓入棧. 如果是運算符, 就從棧里彈出操作數(shù),并執(zhí)行該運算, 然后把結果壓入棧. 當把這樣一個程序分成文件時, 把相關的函數(shù)和變量放在同一個文件中是有意義的.Copyright 2008 W. W. Norton & Company.All rights reserved.6第15章: 編寫大型程序源文件 讀入符號的函數(shù)和相關的處理符號的函數(shù)一起,可以形成一個源文件 (設為token.c). 與棧相關的函數(shù),如 push, pop, make_empty, is_empty, 和 is_full 可以放入一個不同的文件 stack.c. 表示棧的變量也

5、應該放入 stack.c. 主函數(shù)放入另外一個文件 calc.c.Copyright 2008 W. W. Norton & Company.All rights reserved.7第15章: 編寫大型程序源文件 將一個程序分為多個源文件有重大好處: 把相關的函數(shù)和變量放入一個文件中有助于澄清程序的結構. 每個源文件可以單獨編譯,節(jié)省時間. 函數(shù)能夠更容易地用于其他程序中.Copyright 2008 W. W. Norton & Company.All rights reserved.8第15章: 編寫大型程序頭文件 當一個程序分為幾個源文件時,會有如下問題: 一個文件中的

6、函數(shù)怎樣調(diào)用在另一個文件中定義的函數(shù)? 函數(shù)怎樣訪問其他文件中的外部變量? 兩個文件怎樣共享相同的宏定義和類型定義? 答案在于 #include, 它使多個文件共享信息成為可能.Copyright 2008 W. W. Norton & Company.All rights reserved.9第15章: 編寫大型程序頭文件 #include 告訴預處理器把指定的文件內(nèi)容插入進來. 多個文件需要共享的信息可以放入這樣一個文件中. 然后用#include 就可以把這個文件的內(nèi)容包含到每一個源文件中. 這樣被包含進來的文件就叫頭文件 (有時也叫包含文件). 依照慣例,頭文件具有.h擴展名.

7、Copyright 2008 W. W. Norton & Company.All rights reserved.10第15章: 編寫大型程序#include #include 有兩種主要形式. 一種用于C庫中的頭文件:#include 另一種用于所有的其他頭文件:#include filename 兩者的不同之處在于編譯器如何確定頭文件的位置.Copyright 2008 W. W. Norton & Company.All rights reserved.11第15章: 編寫大型程序#include 定位頭文件的典型規(guī)則: #include : 查找系統(tǒng)頭文件所在的路徑.

8、 #include “filename”: 查找當前路徑, 然后查找系統(tǒng)頭文件所在的路徑. 查找頭文件的位置可以改變,通常通過命令行選項,例如 -Ipath.Copyright 2008 W. W. Norton & Company.All rights reserved.12第15章: 編寫大型程序#include 不要用尖括號包含你寫的文件:#include /* WRONG */ 預處理器可能會在系統(tǒng)頭文件所在的地方尋找 myheader.hCopyright 2008 W. W. Norton & Company.All rights reserved.13第15章:

9、編寫大型程序#include #include指示中的文件名可以包含盤符和路徑:#include c:cprogsutils.h /* Windows path */#include /cprogs/utils.h /* UNIX path */ 雖然 #include 中的引號使文件名看起來像字符串,但是預處理器不會那樣處理.Copyright 2008 W. W. Norton & Company.All rights reserved.14第15章: 編寫大型程序#include 通常最好不要在#include包含盤符或路徑. Windows上 #include的壞例子:#inc

10、lude d:utils.h#include cprogsincludeutils.h#include d:cprogsincludeutils.h 好版本:#include utils.h#include .includeutils.hCopyright 2008 W. W. Norton & Company.All rights reserved.15第15章: 編寫大型程序#include #include 還有第三種形式:#include tokenstokens 是任意的預處理符號序列. 預處理器將掃描符號并用找到的宏替換它. 在宏替換之后的#include必須滿足合法形式.

11、 第三種 #include 形式的好處是文件名可以用宏定義,而不必硬編碼.Copyright 2008 W. W. Norton & Company.All rights reserved.16第15章: 編寫大型程序#include 例:#if defined(IA32) #define CPU_FILE ia32.h #elif defined(IA64) #define CPU_FILE ia64.h #elif defined(AMD64) #define CPU_FILE amd64.h#endif#include CPU_FILECopyright 2008 W. W. N

12、orton & Company.All rights reserved.17第15章: 編寫大型程序共享宏定義和類型定義 大多數(shù)大型程序擁有多個源文件共享的宏定義和類型定義. 這些定義應該放入頭文件中.Copyright 2008 W. W. Norton & Company.All rights reserved.18第15章: 編寫大型程序共享宏定義和類型定義 設程序使用宏 BOOL, TRUE, 和 FALSE. 它們的定義可以放入一個頭文件boolean.h中:#define BOOL int#define TRUE 1#define FALSE 0 任何需要這些宏的源

13、文件可以簡單地使用#include boolean.hCopyright 2008 W. W. Norton & Company.All rights reserved.19第15章: 編寫大型程序共享宏定義和類型定義 一個程序的兩個文件包含 boolean.h:Copyright 2008 W. W. Norton & Company.All rights reserved.20第15章: 編寫大型程序共享宏定義和類型定義 在頭文件中,類型定義也很普遍. 例如,我們可以使用 typedef 創(chuàng)建一個Bool 類型,取代BOOL宏. 如果這樣, boolean.h 文件就有如下

14、形式:#define TRUE 1#define FALSE 0typedef int Bool;Copyright 2008 W. W. Norton & Company.All rights reserved.21第15章: 編寫大型程序共享宏定義和類型定義 把宏和類型的定義放入頭文件的好處: 節(jié)省時間. 我們不必拷貝宏到需要的地方. 使程序容易修改. 要改變宏或類型定義,只需要修改頭文件. 避免了因包含同樣一個宏或類型的不同定義而導致的不一致性.Copyright 2008 W. W. Norton & Company.All rights reserved.22第15章

15、: 編寫大型程序共享函數(shù)原型 假設一個源文件含有對函數(shù) f 的調(diào)用,而f 定義在另一個文件 foo.c中. 不加聲明地調(diào)用 f 是危險的. 編譯器認為 f 的返回類型是 int. 它也認為參數(shù)的個數(shù)與調(diào)用f 時的自變量的個數(shù)一致. 自變量被自動地轉(zhuǎn)為默認的類型.Copyright 2008 W. W. Norton & Company.All rights reserved.23第15章: 編寫大型程序共享函數(shù)原型 在調(diào)用f的文件中聲明f可以解決上述問題,但是又會帶來維護的噩夢. 一個好的解決辦法是把 f的原型放入一個頭文件 (foo.h), 再把頭文件包含進需要調(diào)用 f 的文件中.

16、我們也需要在foo.c 中包含foo.h , 使編譯器可以檢查 foo.h中的f原型與foo.c中的定義是否一致.Copyright 2008 W. W. Norton & Company.All rights reserved.24第15章: 編寫大型程序共享函數(shù)原型 如果 foo.c 含有其他的函數(shù),大多可以在 foo.h中聲明. 然而只打算在 foo.c 中使用的函數(shù)不應該聲明在頭文件中.Copyright 2008 W. W. Norton & Company.All rights reserved.25第15章: 編寫大型程序共享函數(shù)原型 可以用逆波蘭式表達式計算器的

17、例子顯示頭文件中函數(shù)原型的使用. stack.c 文件中含有 make_empty, is_empty, is_full, push, 和 pop 函數(shù)的定義. 這些函數(shù)的原型應該放入stack.h 頭文件中:void make_empty(void);int is_empty(void);int is_full(void);void push(int i);int pop(void);Copyright 2008 W. W. Norton & Company.All rights reserved.26第15章: 編寫大型程序共享函數(shù)原型 我們把 stack.h 包含在 calc.c

18、 中,允許編譯器檢查棧函數(shù)調(diào)用. 我們也把 stack.h 包含在 stack.c 中,使編譯器能夠檢驗stack.h中的函數(shù)原型與stack.c中的定義相匹配. Copyright 2008 W. W. Norton & Company.All rights reserved.27第15章: 編寫大型程序共享函數(shù)原型Copyright 2008 W. W. Norton & Company.All rights reserved.28第15章: 編寫大型程序共享函數(shù)原型 要在多個文件中共享一個函數(shù), 我們把它的定義放在一個源文件中, 然后把聲明放在需要調(diào)用它的文件中. 共享外

19、部變量也采用同樣的方式.Copyright 2008 W. W. Norton & Company.All rights reserved.29第15章: 編寫大型程序共享變量聲明 一個聲明和定義變量 i 的例子(使編譯器留出空間):int i; 關鍵字 extern 用于聲明變量(而不是定義它) :extern int i; extern 告訴編譯器 i 是在程序的其他地方定義的, 因此不必為它分配空間.Copyright 2008 W. W. Norton & Company.All rights reserved.30第15章: 編寫大型程序共享變量聲明 當我們使用 ex

20、tern 聲明數(shù)組時, 我們可以省略數(shù)組的長度:extern int a; 因為此時編譯器不為 a 分配空間, 所以不必知道 a 的長度.Copyright 2008 W. W. Norton & Company.All rights reserved.31第15章: 編寫大型程序共享變量聲明 要在幾個源文件中共享變量 i , 我們首先在一個文件里定義 i :int i; 如果 i 需要被初始化, 那么初始化應該放在這個文件里. 其他文件將含有對 i 的聲明:extern int i; 通過在每個文件里聲明 i , 就可以在其他文件里訪問或修改 i .Copyright 2008 W.

21、 W. Norton & Company.All rights reserved.32第15章: 編寫大型程序共享變量聲明 當同一個變量的聲明出現(xiàn)在不同的文件里時, 編譯器不能檢查變量的聲明與定義是否一致. 例如, 一個文件有定義int i;另一個文件有聲明extern long i; 這種錯誤會導致程序不可預知的行為表現(xiàn).Copyright 2008 W. W. Norton & Company.All rights reserved.33第15章: 編寫大型程序共享變量聲明 為了避免不一致, 共享變量的聲明通常放在頭文件里. 一個需要訪問特定變量的源文件可以將適當?shù)念^文件包

22、含進來. 此外, 每一個含有變量聲明的頭文件也被包含進定義變量的源文件中, 使得編譯器能夠檢查二者是否匹配.Copyright 2008 W. W. Norton & Company.All rights reserved.34第15章: 編寫大型程序嵌套包含 一個頭文件可以含有 #include . stack.h 含有下面的原型:int is_empty(void);int is_full(void); 既然這些函數(shù)只返回 0 或 1, 把它們的返回類型聲明為 Bool類型是一個好主意:Bool is_empty(void);Bool is_full(void); 我們需要在sta

23、ck.h 中包含 boolean.h,使得 當stack.h被編譯時 Bool類型生效.Copyright 2008 W. W. Norton & Company.All rights reserved.35第15章: 編寫大型程序嵌套包含 傳統(tǒng)上, C 程序員避開嵌套包含. 然而, 反對嵌套包含的偏見已經(jīng)很大程度地淡化, 部分因為嵌套包含在 C+ 中很普遍.Copyright 2008 W. W. Norton & Company.All rights reserved.36第15章: 編寫大型程序保護頭文件 如果一個源文件包含同一個頭文件兩次, 就會導致編譯錯誤. 當頭文件

24、包含其他的頭文件時,這個問題很普遍. 假設 file1.h 包含 file3.h, file2.h 包含 file3.h, prog.c 包含 file1.h 和 file2.h.Copyright 2008 W. W. Norton & Company.All rights reserved.37第15章: 編寫大型程序保護頭文件當 prog.c 被編譯時, file3.h 將被編譯兩次.Copyright 2008 W. W. Norton & Company.All rights reserved.38第15章: 編寫大型程序保護頭文件 包含同一個頭文件兩次并非總導致編譯

25、錯誤. 如果文件只含有宏定義, 函數(shù)原型和變量聲明, 不會有困難. 然而如果文件含有類型定義, 就會出現(xiàn)編譯錯誤.Copyright 2008 W. W. Norton & Company.All rights reserved.39第15章: 編寫大型程序保護頭文件 為安全起見, 一個好的做法是保護所有的頭文件,避免重復包含. 這樣, 我們可以增加類型定義而不會有忘記保護文件的風險. 此外, 我們很可能節(jié)省了時間,因為避免了不必要的重復編譯.Copyright 2008 W. W. Norton & Company.All rights reserved.40第15章: 編寫

26、大型程序保護頭文件 為了保護頭文件, 我們把文件內(nèi)容放入 #ifndef-#endif 中. 怎樣保護 boolean.h 文件:#ifndef BOOLEAN_H#define BOOLEAN_H#define TRUE 1#define FALSE 0typedef int Bool;#endifCopyright 2008 W. W. Norton & Company.All rights reserved.41第15章: 編寫大型程序保護頭文件 選取與頭文件名相似的宏名是避免與其他宏沖突的好辦法. 既然我們不能為宏取名 BOOLEAN.H, 像 BOOLEAN_H 這樣的名字是

27、個好的選擇.Copyright 2008 W. W. Norton & Company.All rights reserved.42第15章: 編寫大型程序頭文件中的頭文件中的#error 指示 #error 經(jīng)常被放在頭文件里用于檢查頭文件不應被包含的條件. 假設一個頭文件使用了C89標準之前沒有的特征. #ifndef 檢驗_STDC_ 宏是否存在:#ifndef _STDC_#error This header requires a Standard C compiler#endifCopyright 2008 W. W. Norton & Company.All rig

28、hts reserved.43第15章: 編寫大型程序把程序分為文件 設計程序涉及到確定需要哪些函數(shù),并把這些函數(shù)組織成邏輯相關的組. 一旦程序設計好,有個簡單的辦法把程序分成文件.Copyright 2008 W. W. Norton & Company.All rights reserved.44第15章: 編寫大型程序把程序分為文件 每個函數(shù)集形成一個單獨的源文件 (foo.c). 每個源文件有個相應的頭文件 (foo.h). foo.h 含有定義在foo.c中的函數(shù)的原型. 只在 foo.c 中使用的函數(shù)不應該在 foo.h中聲明. 如果一個源文件要調(diào)用foo.c定義的函數(shù),就

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

30、應用于一個名為justify 的文本格式化程序. 假設文件 quote 含有如下輸入: C is quirky, flawed, and anenormous success. Although accidents of history surely helped, it evidently satisfied a need for a system implementation language efficient enough to displace assembly language, yet sufficiently abstract and fluent to describe al

31、gorithms and interactions in a wide varietyof environments. - Dennis M. RitchieCopyright 2008 W. W. Norton & Company.All rights reserved.47第15章: 編寫大型程序程序設計: 文本格式化 在 UNIX 或 Windows 命令行運行程序, 我們輸入如下命令justify quote 符號告訴操作系統(tǒng) justify 將從文件 quote 而不是從鍵盤接收輸入. 這個特性叫做輸入重定向, UNIX, Windows和其他一些操作系統(tǒng)都支持.Copyri

32、ght 2008 W. W. Norton & Company.All rights reserved.48第15章: 編寫大型程序程序設計: 文本格式化 Justify的輸出:C is quirky, flawed, and an enormous success. Althoughaccidents of history surely helped, it evidently satisfied aneed for a system implementation language efficient enoughto displace assembly language, yet

33、sufficiently abstract andfluent to describe algorithms and interactions in a widevariety of environments. - Dennis M. Ritchie justify 的輸出正常顯示在屏幕上,我們可以通過使用輸出重定向把它存儲到一個文件中:justify newquoteCopyright 2008 W. W. Norton & Company.All rights reserved.49第15章: 編寫大型程序程序設計: 文本格式化 justify 將刪除多余的空白和空行,也能填充和調(diào)

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

35、被打印成antidisestablishment*Copyright 2008 W. W. Norton & Company.All rights reserved.51第15章: 編寫大型程序程序設計: 文本格式化 程序不能像它讀單詞那樣一個一個地寫出來. 必須把它們存儲到一個行緩沖區(qū),直到足夠填滿一行.Copyright 2008 W. W. Norton & Company.All rights reserved.52第15章: 編寫大型程序程序設計: 文本格式化 程序的核心是一個循環(huán):for (;) read word; if (cant read word) writ

36、e contents of line buffer without justification; terminate program; if (word doesnt fit in line buffer) write contents of line buffer with justification; clear line buffer; add word to line buffer;Copyright 2008 W. W. Norton & Company.All rights reserved.53第15章: 編寫大型程序程序設計: 文本格式化 程序分成三個源文件: word

37、.c: 與單詞相關的函數(shù) line.c: 與行緩沖區(qū)相關的函數(shù) justify.c: 包含主函數(shù) 我們也需要兩個頭文件: word.h: 定義在word.c中的函數(shù)的原型 line.h: 定義在 line.c中的函數(shù)的原型 word.h 包含一個讀單詞的函數(shù)的原型.Copyright 2008 W. W. Norton & Company.All rights reserved.54第15章: 編寫大型程序word.h#ifndef WORD_H#define WORD_H /* * read_word: Reads the next word from the input and *

38、 * stores it in word. Makes word empty if no * * word could be read because of end-of-file. * * Truncates the word if its length exceeds * * len. * */void read_word(char *word, int len); #endifCopyright 2008 W. W. Norton & Company.All rights reserved.55第15章: 編寫大型程序程序設計: 文本格式化 主循環(huán)顯示需要如下一些函數(shù): 寫行緩沖

39、區(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 2008 W. W. Norton & Company.All rights reserved.56第15章: 編寫大型程序line.h#ifndef LINE_H#define LINE_H /* * clear_line: Clears the current line. * */void clear_line(v

40、oid); /* * add_word: Adds word to the end of the current line. * * If this is not the first word on the line, * * puts one space before word. * */void add_word(const char *word);Copyright 2008 W. W. Norton & Company.All rights reserved.57第15章: 編寫大型程序/* * space_remaining: Returns the number of ch

41、aracters left * * in the current line. * */int space_remaining(void); /* * write_line: Writes the current line with * * justification. * */void write_line(void); /* * flush_line: Writes the current line without * * justification. If the line is empty, does * * nothing. * */void flush_line(void); #en

42、difCopyright 2008 W. W. Norton & Company.All rights reserved.58第15章: 編寫大型程序程序設計: 文本格式化 在我們寫 word.c和line.c文件之前, 我們可以使用在 word.h 和line.h 中聲明的函數(shù)編寫justify.c, 即主程序. 編寫這個文件主要是把最初的循環(huán)設計翻譯成 C.Copyright 2008 W. W. Norton & Company.All rights reserved.59第15章: 編寫大型程序justify.c/* Formats a file of text */

43、#include #include line.h#include word.h #define MAX_WORD_LEN 20 int main(void) char wordMAX_WORD_LEN+2; int word_len; Copyright 2008 W. W. Norton & Company.All rights reserved.60第15章: 編寫大型程序 clear_line(); for (;) read_word(word, MAX_WORD_LEN+1); word_len = strlen(word); if (word_len = 0) flush_l

44、ine(); return 0; if (word_len MAX_WORD_LEN) wordMAX_WORD_LEN = *; if (word_len + 1 space_remaining() write_line(); clear_line(); add_word(word); Copyright 2008 W. W. Norton & Company.All rights reserved.61第15章: 編寫大型程序程序設計: 文本格式化 main 使用如下技巧處理超過20個字符的單詞. 當調(diào)用 read_word時, main 告訴它截去超過21個字符的單詞. 在 re

45、ad_word 返回后, main 檢驗 word 是否含有超過20個字符的串. 如果是, 那么word必然至少21個字符 (被截前), main 把第21個字符換成星號.Copyright 2008 W. W. Norton & Company.All rights reserved.62第15章: 編寫大型程序程序設計: 文本格式化 word.h 頭文件只有一個函數(shù)原型, read_word. read_word 容易編寫,如果我們加一個小的幫助函數(shù) read_char. read_char的任務是讀一個單獨的字符, 如果是一個換行符或制表符, 就轉(zhuǎn)成空格. 讓read_word

46、調(diào)用 read_char (取代 getchar)解決把換行符和制表符看成空格的問題.Copyright 2008 W. W. Norton & Company.All rights reserved.63第15章: 編寫大型程序word.c#include #include word.h int read_char(void) int ch = getchar(); if (ch = n | ch = t) return ; return ch;Copyright 2008 W. W. Norton & Company.All rights reserved.64第15章:

47、編寫大型程序void read_word(char *word, int len) int ch, pos = 0; while (ch = read_char() = ) ; while (ch != & ch != EOF) if (pos len) wordpos+ = ch; ch = read_char(); wordpos = 0;Copyright 2008 W. W. Norton & Company.All rights reserved.65第15章: 編寫大型程序程序設計: 文本格式化 line.c 提供聲明在line.h中的函數(shù)的定義. line.c 也

48、需要跟蹤行緩沖區(qū)狀態(tài)的變量: line: 當前行的字符 line_len: 當前行的字符數(shù) num_words: 當前行的單詞數(shù)Copyright 2008 W. W. Norton & Company.All rights reserved.66第15章: 編寫大型程序line.c#include #include #include line.h#define MAX_LINE_LEN 60 char lineMAX_LINE_LEN+1;int line_len = 0;int num_words = 0; void clear_line(void) line0 = 0; line

49、_len = 0; num_words = 0;Copyright 2008 W. W. Norton & Company.All rights reserved.67第15章: 編寫大型程序void add_word(const char *word) if (num_words 0) lineline_len = ; lineline_len+1 = 0; line_len+; strcat(line, word); line_len += strlen(word); num_words+; int space_remaining(void) return MAX_LINE_LEN

50、 - line_len;Copyright 2008 W. W. Norton & Company.All rights reserved.68第15章: 編寫大型程序void write_line(void) int extra_spaces, spaces_to_insert, i, j; extra_spaces = MAX_LINE_LEN - line_len; for (i = 0; i line_len; i+) if (linei != ) putchar(linei); else spaces_to_insert = extra_spaces / (num_words

51、 - 1); for (j = 1; j 0) puts(line);Copyright 2008 W. W. Norton & Company.All rights reserved.69第15章: 編寫大型程序建造多文件程序 建造大型程序與生成小型程序需要同樣的基本步驟: 編譯 連接Copyright 2008 W. W. Norton & Company.All rights reserved.70第15章: 編寫大型程序建造多文件程序 每個源文件必須分別編譯. 頭文件不必編譯. 當源文件被編譯時,它所包含的頭文件被自動編譯. 對于每一個源文件,編譯器產(chǎn)生一個含有目標代碼

52、的文件. 這些文件稱為目標文件在Unix上具有.o擴展名,在Windows上具有 .obj擴展名.Copyright 2008 W. W. Norton & Company.All rights reserved.71第15章: 編寫大型程序建造多文件程序 連接器合并這些目標文件與庫函數(shù)代碼,生成可執(zhí)行文件. 連接器還負責解析外部引用. 當一個文件中的函數(shù)調(diào)用另一個文件中的函數(shù)或訪問另一個文件中的變量時,就發(fā)生外部引用.Copyright 2008 W. W. Norton & Company.All rights reserved.72第15章: 編寫大型程序建造多文件程序

53、大多數(shù)的編譯器允許一步生成程序. 生成justify的GCC命令:gcc -o justify justify.c line.c word.c 三個源文件首先編譯成目標代碼. 目標文件自動地被送往連接器并被合并成一個單獨的文件. -o 選項指明可執(zhí)行文件命名為 justify.Copyright 2008 W. W. Norton & Company.All rights reserved.73第15章: 編寫大型程序生成文件(Makefiles) 為了使生成大型程序更簡單, UNIX引入生成文件(makefile)的概念. 一個生成文件不但列出屬于程序的文件,而且描述文件間的依賴關系

54、. 假設文件 foo.c 包含文件 bar.h,我們稱foo.c 依賴 bar.h, 因為 bar.h的改變將要求我們重新編譯foo.c.Copyright 2008 W. W. Norton & Company.All rights reserved.74第15章: 編寫大型程序生成文件(Makefiles) justify 程序的UNIX 生成文件:justify: justify.o word.o line.o gcc -o justify justify.o word.o line.o justify.o: justify.c word.h line.h gcc -c just

55、ify.c word.o: word.c word.h gcc -c word.c line.o: line.c line.h gcc -c line.cCopyright 2008 W. W. Norton & Company.All rights reserved.75第15章: 編寫大型程序生成文件(Makefiles) 這些行分成四組; 每組是一個規(guī)則. 每個規(guī)則的第一行給出目標文件, 后面的文件是它所依賴的. 第二行是一個命令,當目標因為它所依賴的文件發(fā)生變化而應該重新生成時,這個命令將被執(zhí)行.Copyright 2008 W. W. Norton & Company

56、.All rights reserved.76第15章: 編寫大型程序生成文件(Makefiles) 在第一個規(guī)則里, justify (可執(zhí)行文件) 是目標:justify: justify.o word.o line.o gcc -o justify justify.o word.o line.o 第一行表明 justify 依賴文件justify.o, word.o, 和 line.o. 在程序建造之后,如果任一個文件發(fā)生變化, justify 需要重新建造. 第二行的命令表明如何再建造.Copyright 2008 W. W. Norton & Company.All righ

57、ts reserved.77第15章: 編寫大型程序生成文件(Makefiles) 在第二個規(guī)則里, justify.o 是目標:justify.o: justify.c word.h line.h gcc -c justify.c 第一行表明,如果justify.c, word.h,或 line.h 發(fā)生變化, justify.o 需要再造. 下面一行顯示如何更新 justify.o (通過重新編譯justify.c). -c 選項告訴編譯器編譯justify.c,但不連接.Copyright 2008 W. W. Norton & Company.All rights reserv

58、ed.78第15章: 編寫大型程序生成文件(Makefiles) 一旦我們?yōu)橐粋€程序創(chuàng)建了生成文件, 我們就能使用 make 建造程序. 通過檢查每個文件的時間和日期, make 能夠確定哪些文件已經(jīng)過時. 于是調(diào)用必要的命令再造程序.Copyright 2008 W. W. Norton & Company.All rights reserved.79第15章: 編寫大型程序生成文件(Makefiles) 生成文件中的每一個命令的前面必須是一個制表符, 不是一系列空格. 生成文件通常名為Makefile (或 makefile). 當使用 make 時, 它自動地在當前路徑中檢查具有

59、這樣的名字的文件.Copyright 2008 W. W. Norton & Company.All rights reserved.80第15章: 編寫大型程序生成文件(Makefiles) 要調(diào)用 make, 使用命令make target其中 target 是生成文件中的一個目標. 如果調(diào)用make時沒有指定目標, 它將建造第一個規(guī)則的目標. 除了第一個規(guī)則的這個特性,生成文件里的其他規(guī)則的順序是任意的.Copyright 2008 W. W. Norton & Company.All rights reserved.81第15章: 編寫大型程序生成文件(Makefile

60、s) 真實得生成文件并非總是容易理解. 有許多技術可以減少生成文件的冗余并使它們?nèi)菀仔薷? 這些技術大大降低了生成文件的可讀性. 生成文件的替代物包括某些集成開發(fā)環(huán)境支持的工程文件.Copyright 2008 W. W. Norton & Company.All rights reserved.82第15章: 編寫大型程序連接中的錯誤 有些錯誤在編譯時不能被發(fā)現(xiàn),而在連接時被發(fā)現(xiàn). 如果一個函數(shù)或變量的定義丟失,連接器將無法解析對它的外部引用 結果是這樣一個消息 “undefined symbol” 或 “undefined reference.”Copyright 2008 W.

61、W. Norton & Company.All rights reserved.83第15章: 編寫大型程序連接中的錯誤 連接錯誤的普遍原因:拼寫錯誤拼寫錯誤. 如果一個變量或函數(shù)名拼寫錯, 連接器將報告丟失.文件丟失文件丟失. 如果連接器找不到foo.c中的函數(shù), 它也許不知道這個文件.庫丟失庫丟失. 連接器可能不能找到程序中使用的所有的庫函數(shù). 在UNIX中, 當使用的程序被連接時,需要指定 -lm 選項.Copyright 2008 W. W. Norton & Company.All rights reserved.84第15章: 編寫大型程序重建程序 在程序開發(fā)中, 我們很少需要編譯所有的文件. 為了節(jié)省時間, 再造過程應該只編譯那些被最近的修

溫馨提示

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

評論

0/150

提交評論