杜友福等編著的《C語言程序設計(第3版)》是《C語言程序設計》的第三版,為C語言程序設計課程教材。全書共分13章,全面地介紹了C語言的基本語法及C語言程序的設計方法,內容包括:C語言程序設計概述,數據類型、運算符與表達式,順序結構程序設計,選擇結構程序設計,循環結構程序設計,數組,函數,用戶標識符的作用域和存儲類,編譯預處理,指針,結構體、共用體和用戶定義類型,位運算,文件。每章后面均配有大量的習題,附錄中介紹了C語言的關鍵字、AscII代碼表、運算符及其優先級和結合性、C語言的常用庫函數。為了使于教學和自學,本書配有《C語言程序設計導學》(第三版)。 《C語言程序設計(第3版)》特別適舍于本、專科非計算機專業的初學者,也可供計算機等級考試者和其他各類學習者使用參考。
計算機知識已成為在校學生必須掌握的一門技能,要能熟練地使用計算機,特別是利用計算機開發軟件,必須至少熟練地掌握一門計算機語言。 杜友福等編著的《C語言程序設計(第3版)》為《C語言程序設計》的修訂第三版,全書共十三章,全面地介紹了C語言的基本語法及C語言程序的設計方法。每章后面均配有大量的習題,在做選擇題時,不僅要選擇答案,更重要的是要學會分析,為什么是對的?錯在什么地方?這樣才有收獲。在做程序填空題時,最好是將填空后的完整程序在機器上調試一下,這樣有助于對程序及算法的分析和理解。編程題,要自己獨立編寫并上機調試。通過一系列環節的訓練,才能達到學好C語言的目的。
第1章 C語言程序設計概述
1.1 程序和程序設計語言
1.1.1 程序與程序設計
1.1.2 程序設計語言
1.1.3 語言處理程序
1.1.4 設計程序的基本原則
1.2 算法
1.2.1 算法及算法的特性
1.2.2 算法的表示
1.3 結構化程序設計方法
1.4 C程序的基本結構
習題1
第2章 數據類型、運算符與表達式
2.1 C語言的數據類型
2.2 常量、變量與標識符
2.3 整型數據
2.4 實型數據
2.5 字符型數據
2.6 算術運算符和算術表達式
2.7 賦值運算符與賦值表達式
2.8 逗號運算符與逗號表達式
2.9 求字節數運算符
習題2
第3章 順序結構程序設計
3.1 C語句概述
3.2 賦值語句
3.3 數據的輸入與輸出
3.3.1 printf函數
3.3.2 putchar函數
3.3.3 scanf函數
3.3.4 getchar函數
3.4 順序結構程序舉例
習題3
第4章 選擇結構程序設計
4.1 關系運算和邏輯運算
4.1.1 邏輯值及其在c語言中的表示
4.1.2 關系運算符與關系表達式
4.1.3 邏輯運算符與邏輯表達式
4.2 if語句
4.2.1 if語句的三種常用形式
4.2.2 if語句的嵌套
4.3 條件運算符和條件表達式
4.4 switch語句
4.4.1 switch語句的一般形式與執行過程
4.4.2 switch語句體中的break語句
4.5 選擇結構程序舉例
習題4
第5章 循環結構程序設計
5.1 語句標號、goto語句及用goto語句構成的循環
5.2 while語句和用while語句構成的循環
5.3 do—while語句和用do—while語句構成的循環
5.4 for語句和用for語句構成的循環
5.5 循環結構的嵌套
5.6 break語句和continue語句在循環體中的作用
5.6.1 break語句
5.6.2 continue語句
5.7 循環結構程序舉例
習題5
第6章 數組
6.1 一維數組
6.1.1 一維數組的定義
6.1.2 一維數組元素的引用
6.1.3 一維數組的初始化
6.1.4 一維數組的定義和數組元素引用舉例
6.2 二維數組
6.2.1 二維數組的定義
6.2.2 二維數組元素的引用
6.2.3 二維數組的初始化
6.2.4 二維數組的定義和數組元素引用舉例
6.3 字符數組
6.3.1 字符數組的定義與引用
6.3.2 字符數組的初始化
6.3.3 用字符數組來存放字符串
6.3.4 字符數組的輸入與輸出
6.3.5 用于字符串處理的函數
6.3.6 字符數組應用舉例
習題6
第7章 函數
7.1 庫函數
7.2 函數的定義和返回值
7.2.1 函數的定義
7.2.2 函數的返回值
7.3 函數的調用
7.3.1 函數調用的一般形式
7.3.2 函數調用的方式
7.4 函數的聲明
7.4.1 函數聲明的形式
7.4.2 函數聲明的位置
7.5 調用函數和被調用函數之間的數據傳遞
7.5.1 變量作為參數
7.5.2 數組名作為參數
7.6 函數的嵌套調用與遞歸調用
7.6.1 函數的嵌套調用
7.6.2 函數的遞歸調用
7.7 程序舉例
習題7
第8章 用戶標識符的作用域和存儲類
8.1 局部變量、全局變量和存儲分類
8.1.1 用戶標識符的作用域及變量的生存期
8.1 一局部變量、全局變量和存儲分類
8.2 局部變量及其作用域和生存期
8.2.1 auto變量
8.2.2 register變量
8.2.3 靜態存儲類的局部變量
8.3 全局變量及其作用域和生存期
8.3.1 全局變量的作用域和生存期
8.3.2 全局變量的作用域擴展
8.3.3 靜態全局變量
8.4 外部函數與內部函數
8.4.1 外部函數
8.4.2 內部函數
習題8
第9章 編譯預處理
9.1 宏定義
9.1.1 不帶參數的宏定義
9.1.2 帶參數的宏定義
9.1.3 終止宏定義
9.2 文件包含
9.3 條件編譯
習題9
第10章 指針
10.1 指針的基本概念
1O.2 指針變量的定義與引用
10.2.1 指針變量的定義
10.2.2 指針變量的引用
10.2.3 指針變量的賦值運算
10.2.4 二重指針變量
10.2.5 空指針和void類型指針
10.3 函數之間地址值的傳遞
10.3.1 指針變量作為函數參數
10.3.2 返回地址值的函數
10.4 一維數組和指針
10.4.1 通過指針引用數組元素
10.4.2 數組名或指針變量作形參
lO.4.3 使用指針變量處理一維數組的應用舉例
10.4.4 用指向字符的指針變量處理字符串
10.4.5 使用指針變量處理字符串應用舉例
10.5 多維數組和指針
10.5.1 二維數組和數組元素的地址
10.5.2 通過指向數組元素的指針引用二維數組
10.5.3 通過行指針引用二維數組
10.5.4 指針數組
10.6 函數和指針
10.6.1 用函數指針變量調用函數
10.6.2 用指向函數的指針作函數參數
10.7 小結
10.7.1 指針基本概念與性質小結
10.7.2 有關指針的數據類型的小結
10.7.3 指針運算的小結
習題10
第ll章 結構體、共用體和用戶定義類型
11.1 結構體類型及結構體變量
11.1.1 結構體類型的定義
11.1.2 結構體類型變量的定義
11.1.3 結構體變量的內存空間大小
11.1.4 結構體變量的引用
11.1.5 結構體變量的初始化
11.2 結構體數組
11.3 指向結構體的指針
11.3.1 指向結構體變量的指針
11.3.2 指向結構體數組的指針
11.4 結構體與函數
11.5 鏈表
11.5.1 鏈表概述
11.5.2 鏈表的基本操作
11.6 共用體
11.6.1 共用體類型的定義
11.6.2 共用體類型變量的定義
11.6.3 共用體變量的引用
11.7 枚舉類型
11.8 用typedef定義一種新類型名
習題11
第12章 位運算
12.1 位運算符與位運算
12.2 位運算舉例
12.3 位段
習題12
第13章 文件
13.1 C語言文件的基本概念
13.2 文件指針
13.3 文件的打開與關閉
13.3.1 文件的打開(foperl函數)
13.3.2 文件的關閉(fclose函數)
13.4 文件的讀寫
13.4.1 字符的輸入與輸出(fputc函數、fgetc函數、putc函數、getc函數)
13.4.2 檢查文件是否結束(feof函數)
13.4.3 字符串的輸入與輸出(fgets函數和fputs函數)
13.4.4 二進制數據塊的輸入與輸出(fread函數和fwrite函數)
13.4.5 格式化的文件輸入與輸出(fscanf函數和fprintf函數)
13.5 文件的定位
13.5.1 改變文件讀寫位置(fseek函數)
13.5.2 ftell函數
13.5.3 反繞(rewind函數)
13.6 文件的出錯檢測
13.6.1 出錯檢測(ferror函數)
13.6.2 清除錯誤標志(clearerr函數)
習題13
附錄A C語言的關鍵字
附錄B ASCII代碼表
附錄C 運算符及其優先級和結合性
附錄D C語言的常用庫函數
1.1 程序和程序設計語言
計算機是在程序的控制下進行工作的。要想讓一臺通用的計算機能夠正常運行,需要預先安裝好一系列的系統程序,如Windows 系統等,它們是由計算機的專業人員編制出來的程序。一般的用戶如果要想利用計算機來解決某個具體的實際問題,就需要用戶自己動手編寫相應的應用程序。程序設計是計算機應用人員的一項基本功,也是當代大學生必須經歷的一項基本的思維方式訓練。只有學會了程序設計,才能更深入地使用計算機。
1.1.1 程序與程序設計
所謂程序,就是用計算機能夠識別的語言所描述的解決某個特定問題的方法和步驟,是由一組相關的指令組成的。通常一個程序主要描述兩個方面的內容:一是描述問題的每個對象及它們之間的關系,即數據結構的內容;二是描述對這些對象進行處理的動作、動作的先后順序,即求解的算法。因此,程序也可以用經典的公式來表示:程序= 數據結構+ 算法所謂程序設計,就是利用計算機解決問題的全過程。通常是對問題進行分析并建立數學模型;在此基礎上設計出相應的數據結構和算法;然后用某種程序設計語言編寫相應的程序代碼;最后調試程序,使之運行后能產生預期的結果。
1.1.2 程序設計語言
程序設計語言是人們根據計算機的特點及描述問題的需要設計出來的、介于人與計算機之間的一種中間語言。隨著計算機技術的發展,涌現出了幾百種不同風格的程序設計語言,最常用的不過十多種。按照程序設計語言的發展過程,可以將它們分為以下四類。
1.機器語言
機器語言是由二進制代碼按一定的規則組成的、能被機器直接理解和執行的指令的集合。機器語言中的每一條語句實際上是一條二進制形式的指令代碼(即機器指令) ,機器指令一般由操作碼和操作數兩個部分組成,操作碼表示該指令所要完成的功能,操作數指出完成這個功能所需要的數據或數據在內存中的地址。
例如,某種計算機上計算A = 15 + 10 的機器語言程序如下:10110000 00001111
把15(其二進制值為00001111)放入累加器A中00101100 00001010 把10與累加器A中的值相加,結果仍放入A中11110100 結束,停機用機器語言編寫的程序,計算機可以直接識別和執行,因而執行效率高。但由于機器語言指令跟具體的計算機內部結構有關,不同類型的計算機,其指令系統不同,因此機器語言程序通用性差。另外,機器語言的指令不直觀,難認、難記、難理解、難修改、易出錯。
因此,很少有人用機器語言直接編程。
2.匯編語言
為了便于理解和記憶,人們采用能夠幫助記憶的指令助記符來代替機器語言指令代碼中的操作碼,用地址標識符或十進制數來代替操作數。指令助記符一般采用描述該指令功能的英文單詞的縮寫,如用ADD 表示加法操作、SUB 表示減法操作、JMP 表示程序跳轉等。這種采用指令助記符的語言就是匯編語言,又稱為符號語言。
例如,上述計算A = 15 + 10 的匯編語言程序如下:MOV A,15 把15放入累加器A中ADD A,10 把10與累加器A中的值相加,結果仍放入A中HLT 結束,停機計算機不能直接執行用匯編語言編寫的程序,必須由一種專門的翻譯程序(即匯編程序)將匯編語言源程序翻譯成機器語言程序,計算機才能執行。
3.高級語言
機器語言和匯編語言都是面向機器的語言,一般稱其為低級語言。它們對計算機硬件結構的依賴性很大,這就要求程序的開發者必須熟悉和了解計算機硬件的內部結構,因此,它們面對的用戶一般是計算機專業人員,用它們開發出來的程序通用性也比較差。
隨著計算機技術的發展及計算機應用領域的不斷擴大,計算機用戶的隊伍也在不斷壯大,而且這個隊伍中絕大部分人不是計算機專業人員。為此,從20 世紀50 年代中期開始,逐步發展并出現了多種面向過程的程序設計語言,稱為高級語言。高級語言與具體的計算機硬件無關,其表達方式接近于被描述的問題,接近于自然語言和數學語言,易為人們接受和掌握。使用高級語言,只要關心描述問題的求解過程,不必關心計算機的內部結構。因此,用高級語言編寫程序要比用低級語言容易得多,大大簡化了程序的編制和調試過程,使編程效率得到大幅度的提高。
例如,上述計算A = 15 + 10 的BASIC 語言程序如下:A=15+10 '15與10相加的結果放入A中PRINT A '輸出AEND '程序結束用高級語言編寫的程序易學、易讀、易修改,通用性好。但是,計算機同樣不能直接執行高級語言程序,必須經過語言處理程序的翻譯后才能被計算機接受。
下面列出幾種最常用的高級語言及其最適用的領域:BASIC 教學和微小型應用程序的開發FORTRAN 科學及工程計算程序的開發PASCAL 專業教學和應用程序的開發C 中、小型系統程序的開發FoxPro 數據庫應用程序的開發COBOL 商業、交通和銀行等行業應用程序的開發LISP 人工智能程序的開發Prol og 人工智能程序的開發4.面向對象的高級語言面向對象的程序設計語言是在早期的高級語言(面向過程)的基礎上發展而來的。面向對象的程序比面向過程的程序更清晰、易懂,更適宜于編寫大規模的應用程序,正在成為當代主流的程序設計工具。常用的面向對象的高級語言主要有:Visual Basic ,VisualFoxPro ,Visual C + + ,Java 和C # 等。
面向對象的程序設計方法是一種結構模擬方法,它把現實世界模擬成由許多對象組成的實體,對象與對象之間通過互相發送和接收消息進行聯系;消息激發對象本身的運動,形成對象狀態的變化。從程序設計的角度來看,每個對象都是一個由若干數據和方法組成的封裝體―― 抽象數據類型。
目前,國內外大多數計算機上運行的程序,大多采用高級語言編寫。因此,應當熟練掌握用高級語言編寫程序的方法和技巧。由于面向過程的語言是程序設計的基礎,所以本書以面向過程的C 語言為背景,介紹程序設計的基本概念和基本方法。
1.1.3 語言處理程序
計算機只能直接識別和執行機器語言程序。用匯編語言和高級語言編寫的程序,都必須經過一個翻譯過程才能轉換為計算機所能識別的機器語言程序,實現這個翻譯過程的工具是語言處理程序,即翻譯程序。翻譯程序除了要完成語言間的轉換外,還要進行語法、語義等方面的檢查。針對不同的程序設計語言編寫出的源程序,有各自對應的翻譯程序,不能互相通用。翻譯程序可分為匯編程序和高級語言翻譯程序;高級語言翻譯程序有兩種工作方式,即解釋方式和編譯方式,對應的是解釋程序和編譯程序。
1.匯編程序
匯編程序是一種由專業的軟件開發商提供的系統軟件,它能將用匯編語言編寫的源程序翻譯成某種類型的計算機的機器語言程序,即目標程序,這一翻譯過程稱為匯編。圖1.1 是匯編語言源程序的匯編、連接與運行過程示意圖。
匯編程序通常是將一條匯編語言指令翻譯成一條機器語言指令。在翻譯的過程中,還要對每一條指令進行語法檢查,若有錯誤,就中斷匯編過程,并以某種方式輸出錯誤的類型及有關信息,以便用戶進行修改。若沒有錯誤,就自動生成相應的目標程序,再經過連接處理之后,便可以運行了。運行的是匯編并連接后產生的可執行程序,若源程序作了某些修改,則必須再重新進行匯編與連接處理。
2.解釋程序
解釋程序的工作過程是將高級語言源程序一句一句地讀入,每讀入一個語句都要對它進行分析和解釋,若有錯誤就即時中斷其解釋過程,并通知用戶進行修改,若沒有錯誤就按照解釋結果執行所要求的操作,其工作過程如圖1.2 所示。
BASIC 、LISP 等語言采用解釋方式。解釋方式靈活、方便,交互性好,解釋執行程序的過程中也不會產生目標程序。但因為是邊解釋邊執行,所以程序的執行速度很慢,如果源程序中出現循環結構,解釋程序也要重復多次地解釋循環體中的每一條語句,造成很大浪費;而且解釋方式在程序運行期間始終離不開翻譯程序。
3.編譯程序
編譯程序的功能是將用高級語言編寫的源程序翻譯成機器語言程序,即目標程序,這一翻譯過程稱為編譯。但目標程序還不能立即裝入機器執行,因為還沒有連接成一個整體,在目標程序中還可能要調用一些其他語言編寫的程序和標準程序庫中的標準子程序。
連接程序的作用是將目標程序與有關的程序庫組合成一個完整的可執行程序,產生的可執行程序可以脫離編譯程序和源程序獨立存在并可反復使用。由于經過編譯并連接后所產生的可執行程序具有運行速度快、保密性好等優點,大多數高級語言都采用編譯方式,如C/C + + ,PASCAL ,FORTRAN ,COBOL 等。但每次修改源程序后,都必須重新進行編譯與連接處理,其工作過程如圖1.3 所示。
與匯編程序不同的是,編譯程序往往需要將一條高級語言的語句轉換成若干條機器語言指令,而且翻譯的過程也要復雜得多。
1.1.4 設計程序的基本原則要設計出一個好的程序,必須了解利用計算機解決實際問題的過程,掌握程序設計的基本技術,并熟練地掌握一種程序設計語言。計算機解決問題的基本過程如圖1.4 所示。
如何才能編寫出高質量的程序呢? 下面是設計程序時應遵循的基本原則:
(1) 正確性。正確性是指程序本身必須具備且只能具備程序設計規格說明書中所列舉的全部功能。它是判斷程序質量的首要標準。
(2) 可靠性。可靠性是指程序在多次反復使用過程中不失敗的概率。
(3) 簡明性。簡明性的目標是要求程序簡明易讀。
(4) 有效性。程序在計算機上運行需要使用一定數量的計算機資源,如CPU 的時間、存儲器的存儲空間。有效性就是要在一定的軟、硬件條件下,反映出程序的綜合效率。
(5) 可維護性。程序的維護可分為校正性維護、適應性維護和完善性維護。一個軟件的可維護性直接關系到程序的可用性,因此應特別予以關注。
(6) 可移植性。程序主要與其所完成的任務有關,但也與它的運行環境有著一定的聯系。軟件的開發應盡可能遠離機器的特征,以提高它的可移植程度。
1.2 算 法為了有效地進行程序設計,不僅要掌握一門程序設計語言,還應該學會對各類問題擬定出有效的解題步驟,即算法設計。有了正確的算法,才能夠編制程序。算法的好壞決定了程序的優劣,因此程序設計的核心任務之一就是設計算法。
1.2.1 算法及算法的特性
1.算法所謂算法,就是問題的求解方法。例如,期末考試前的復習計劃,就是“復習算法” ;到醫院看病,先掛號,后診斷并開出藥方,最后再取藥等,是“看病算法” 。在計算機中,把解題過程準確的、完整的描述稱為解題算法。
一個算法由一些操作組成,而這些操作又是按一定的控制結構所規定的次序執行的,也就是算法由操作與控制結構兩個要素組成。
2.算法的特性一個算法應該具有如下特點:
(1) 有窮性。一個算法應該包含有限的操作步驟,而不能是無限的;并且能在有限的時間內完成。
(2) 確定性。每一個步驟都是確定的,而不能是含糊的、模棱兩可的。
(3) 有效性。每一個步驟都能得到有效地執行,并得到確定的結果。
(4) 有零個或多個輸入。所謂輸入是指執行算法時需要從外界取得必要的信息。
(5) 有一個或多個輸出。算法的目的就是為了求解,“解”就是輸出。
上述所講的算法特性約束人們去正確地設計算法,使之能夠正確無誤地執行,達到求解問題的預期效果。同時,算法還應該具有直觀、清晰、易懂的表示形式,以利于交流、維護與修改。
1.2.2 算法的表示表示算法的方法有許多種,如自然語言、偽代碼、傳統流程圖、結構化流程圖等。
1.自然語言自然語言表示法是表示算法的最原始方法。自然語言就是人們日常使用的語言,可以是漢語、英語或其他自然語言。
例1.1 給出求x1 + x2 + x3 + x4 + x5 的值的算法。
算法分析如下:
(1) 適合手工計算的算法。解題步驟為:
① 將x1 與x2 相加,得到兩個數之和;
② 將第① 步的和與x3 相加,得到三個數之和;
③ 將第② 步的和與x4 相加,得到四個數之和;
④ 將第③ 步的和與x5 相加,得到五個數之和。
這種算法雖然是正確的,但太煩瑣。如果要求x1 + x2 + … + x100 ,則要寫99 個步
驟,顯然是不可取的。
(2)適合計算機處理的算法。該算法能更加簡潔地表達上述解題過程,并具有通用
性。先定義幾個變量:設置變量s 表示多項式之和,其初值為零;設置變量a 表示多項式
中的一項,它的值可以為x1 ,x2 ,… ,x5 ;用i 記錄被加了幾次,其初值為1 。解題步驟為:
① s 0 ;
② i 1 ;
③ a xi ; (使a 等于多項式中的第i 項)
④ s s + a ;(求和,并將結果保留在s 中)
⑤ i i + 1 ;(計數增值)
⑥ 若i ≤ 5 ,則重復③ 、④ 、⑤ 步,否則計算結束;
⑦ 輸出s 。
這個算法就比較靈活,如果想求出100 個數據的累加和,只需將上述算法的第⑥ 步中
的“i ≤ 5”改為“i ≤ 100”即可。
例1.2 求兩個正整數m 和n 的最大公約數的歐幾里得算法。
歐幾里得算法可描述為:
① 從鍵盤輸入兩個正整數m 和n 的值;
② 求出m 除以n 的余數r ;
③ 若r = 0 ,則轉至第⑥ 步,否則,執行第④ 步;
④ 用n 取代m ,用r 取代n ;
⑤ 轉到第② 步,繼續求新的m 和n 的最大公約數;
⑥ 輸出n 的值,n 的值即為當初那兩個數的最大公約數。
用自然語言表示的算法通俗易懂,但文字冗長,容易出現“歧義性” ,在表示包含分支
結構和循環結構的算法時,不太方便和直觀。因此,除了很簡單的問題以外,一般不用自
然語言來表示算法。
2.偽代碼
偽代碼是一種介于自然語言和計算機語言之間的、用文字和符號表示的算法。算法中的每一行(或幾行)表示一個基本操作,它可以是一行文字,也可以是一個數學公式,還可以是一個某種計算機語言的語句等。它不用圖形符號,因此書寫方便,格式緊湊,也比較好懂,便于向計算機語言描述的算法(即程序)過渡。但讀者必須先了解一種計算機語言之后,才能讀懂這些算法,現代的程序設計一般不采用偽代碼表示方法。