Intermediate C Programming為什么要寫這本書市面上有成百上千種關于編程的書籍,其中有很多都是關于C語言編程的,那么為什么我還要寫這本書呢?為什么建議你花時間讀它呢?這本書跟其他書有什么不同呢?跟很多作者一樣,我寫這本書是因為我覺得有必要,覺得這本書中的方法比其他書中的更好。
我將現在已有的關于編程的書分為兩類:入門和進階。入門類書是給初學者寫的,一般都假設讀者沒有編程基礎,所以主要是介紹基本的概念。通常以“Hello World!”程序開始,也就是將“Hello World!”輸出到電腦屏幕的程序。這種類型的書主要是一步步地介紹語言特點,包括關鍵詞、數據類型、控制結構、字符串、文件操作等,而這些書一般都有一個特點:程序很短,一般是1~2頁。這很奏效,因為短程序有助于解釋編程語言的新概念。如果把學編程語言比作學自然語言,如英語、漢語、法語、韓語等,這些書就相當于教導如何造句和撰寫短段落。
第二類書是寫給有程序開發經驗的讀者的。這些書主要介紹解決現實中的問題的程序,比如關于電腦游戲或者圖像。而這類書的例子一般很長,有些甚至幾千行代碼,因此不會全部印在書本上。書中只會解釋程序的其中一部分,而源程序一般保存在CD或者某個網址上。這類書一般不會再介紹如何編程,而是大多專注于解決特定問題的算法研究,有時包括算法性能的詳細信息。讀者不可能再找到類似于“Hello World!”這樣的例子。再比作自然語言的例子,這類書就是在教導如何撰寫可能超過20頁的短篇小說。
問題是,從寫一個段落到寫一篇小說,這種跨越太難了。
一本針對中級編程能力的學生的書市面上很少有針對中級編程能力學生的書籍。這些學生往往已經掌握了編程的基本知識,在看到if或者while時不會茫然,知道如何創建函數和調用函數,有能力編寫幾十上百行的短代碼,卻不知道如何處理上千行的程序。他們經常會犯錯誤,因為大多數入門級的書籍只教導如何編寫正確的程序,卻不會教導避免常見的錯誤。他們往往對大多數的概念和那些可以幫助提高編程能力的工具都不太熟悉,他們需要這樣一個臺階:可以幫助他們從有能力編寫短代碼到有能力編寫解決現實問題的程序。
現在入門和進階的空檔已經被數據結構和算法的書籍填充了一部分,這類圖書一般提供實現數據結構或算法的完整例子。然而這并不是最合適的解決方法,這類圖書致力于介紹數據結構和算法,卻罕有提供幫助讀者編寫正確代碼的信息。事實上,它們大多只提供程序,而很少解釋。它們往往不解釋編程概念,比如函數需要一個指針作為實參的原因或者深拷貝與淺拷貝之間的差異等。因此,讀者只能自學這些編程技巧。
為了迎合這個需求,我寫下這本針對中級編程能力的學生的書,本書適合作為學習編程的第二本教材。
避免出錯和調試的重點我們可以看到有很多關于如何編程的書籍,卻很少關于開發軟件的書籍。開發軟件不是簡單地輸入代碼,它需要更多的知識和技能。為了彌補這種不足,最好就是去研究什么是對的、什么是錯的。只解釋如何編寫正確的程序是不夠的,還需要解釋常見的錯誤并將它們與正確的程序進行對比。
一次疏忽可能使程序運行出乎意料,甚至是某些情況下運行正確而另一些情況下出錯。這種類型的錯誤往往很難發現,更別說更正了。本書將介紹一些常見的錯誤以教導讀者如何避免這些錯誤。調試過程在大多數書中都不會涉及,罕有書籍會提到“調試器”這個詞,以至于有些讀者都不知道這類工具的存在。學會如何使用調試器一般不超過30分鐘,這可以幫助程序員節省很多時間。關于如何使用調試器和調試策略的書籍則更少了。
程序設計和離散數學程序設計和離散數學是計算機科學中的兩個重要學科,然而,大多數書籍都將這兩個主題分開,所以很少會在編程的書籍中看到數學公式,同樣也很難在離散數學中看到代碼。在本書中,這兩個主題緊密結合,我相信讀者可以從中學到更多的知識。
為什么本書使用C語言?C語言誕生于20世紀60年代后期和20世紀70年代早期。在C語言發明之后,很多語言也相繼出現,這些語言也深受C語言的影響。除了它的歷史影響之外,C語言的簡單易用也保證了它在幾乎所有現代化平臺中的重要地位。與許多操作系統一樣,Linux是就用C語言編寫的,Android基本都是用Java編寫的但仍有叫作JNI(Java Native Interface,Java本地接口)的C語言接口。大多數計算機語言都可以與C語言進行通信或通過C語言進行通信,事實上這對一種編程語言而言是有用的,因為大多數操作系統接口都使用C語言。當一個全新的系統被設計出來,C語言通常是第一種(很多情況下是唯一一種)被系統支持的編程語言。
……
Yung-Hsiang Lu(陸永祥),現為普度大學電子和計算機工程學院副教授。他是ACM杰出科學家和杰出講者, IEEE重啟計算計劃(Rebooting Computing initiative)成員。他還是2015年首屆低功耗圖像識別競賽的主要組織者,IEEE多媒體通信技術委員會的多媒體通信系統興趣組的主席(2014-2016)。他2002年獲得了斯坦福大學電子工程系博士學位。
目 錄
Intermediate C Programming
出版者的話
譯者序
序
前言
第一部分 計算機存儲:內存和文件
第1章 程序的執行 2
1.1 編譯 2
1.2 重定向輸出 6
第2章 棧內存 7
2.1 值和地址 7
2.2 棧 8
2.3 調用棧 9
2.3.1 返回位置 9
2.3.2 函數實參 12
2.3.3 局部變量 14
2.3.4 值地址 15
2.3.5 數組 16
2.3.6 獲取地址 17
2.4 可見度 17
2.5 習題 20
2.5.1 繪制調用棧I 20
2.5.2 繪制調用棧II 20
2.5.3 地址 21
2.6 習題解答 21
2.6.1 繪制調用棧I 21
2.6.2 繪制調用棧II 22
2.6.3 地址 22
2.7 在DDD(命令行調試程序)上檢測調用棧 22
第3章 預防、檢測及消除bug 26
3.1 開發軟件≠編碼 26
3.1.1 編程前 26
3.1.2 編程中 27
3.1.3 編程后 28
3.2 常見錯誤 28
3.2.1 未初始化變量 28
3.2.2 錯誤數組下標 28
3.2.3 錯誤數據類型 28
3.3 后執行式和交互式調試 28
3.4 生產代碼與測試代碼分離 29
第4章 指針 30
4.1 作用域 30
4.2 swap函數 31
4.3 指針 33
4.4 再論swap函數 37
4.5 類型錯誤 39
4.6 數組和指針 40
4.7 類型規則 43
4.8 指針運算 44
4.9 習題 47
4.9.1 swap函數1 47
4.9.2 swap函數2 48
4.9.3 swap函數3 48
4.9.4 swap函數4 48
4.9.5 swap函數5 49
4.9.6 15 552種變化 49
4.10 習題解答 50
4.10.1 swap函數1 50
4.10.2 swap函數2 50
4.10.3 swap函數3 51
4.10.4 swap函數4 51
4.10.5 swap函數5 51
第5章 編寫和測試程序 52
5.1 不同的數組元素 52
5.1.1 main函數 52
5.1.2 areDistinct函數 53
5.1.3 編譯和鏈接 54
5.1.4 make工具 55
5.2 使用Makefile測試 57
5.2.1 生成測試用例 58
5.2.2 重定向輸出 58
5.2.3 使用diff去比較輸出 58
5.2.4 添加測試到Makefile 59
5.3 無效的內存訪問 60
5.4 使用valgrind檢查內存訪問錯誤 62
5.5 測試覆蓋 64
5.6 限制內核大小 67
5.7 帶有死循環的程序 67
第6章 字符串 69
6.1 字符數組 69
6.2 C語言中的字符串函數 72
6.2.1 復制函數:strcpy 72
6.2.2 比較函數:strcmp 73
6.2.3 尋找子字符串函數:strstr 73
6.2.4 尋找字符函數:strchr 74
6.3 理解argv 74
6.4 對子字符串計數 77
第7章 編程問題和調試 80
7.1 實現字符串函數 80
7.1.1 C語言庫 80
7.1.2 頭文件 80
7.1.3 mystring.h 82
7.1.4 創建輸入和正確輸出 82
7.1.5 Makefile 86
7.1.6 mystring.c 86
7.1.7 使用const 88
7.2 調試 89
7.2.1 找到死循環 90
7.2.2 找到無效內存訪問 91
7.2.3 檢測無效內存訪問 92
第8章 堆內存 94
8.1 用malloc函數創建數組 94
8.2 棧和堆 96
8.3 返回堆地址的函數 98
8.4 C語言中的二維數組 99
8.5 指針和參數 101
第9章 使用堆內存的編程問題 104
9.1 對數組排序 104
9.1.1 生成測試輸入和期望輸出 104
9.1.2 重定向輸入 105
9.1.3 整數排序 107
9.1.4 使用valgrind檢測內存泄漏 110
9.2 使用qsort進行排序 111
9.2.1 qsort 111
9.2.2 比較函數 112
9.2.3 執行范例 114
9.2.4 對字符串排序 115
第10章 讀寫文件 118
10.1 通過argv傳遞一個文件名 118
10.2 讀取文件 119
10.2.1 讀取字符型:fgetc 119
10.2.2 讀取整型:fscanf(...%d...) 121
10.3 寫入文件 123
10.4 讀寫字符串 125
第11章 編程解決使用文件的問題 128
11.1 對文件中的整數進行排序 128
11.2 計算字符出現的次數 130
11.3 計算單詞出現的次數 132
11.4 如何注釋程序 134
第二部分 遞歸
第12章 遞歸 138
12.1 在限制條件下選取小球 138
12.1.1 雙色球問題 138
12.1.2 三色球問題 139
12.1.3 附加限制條件 140
12.2 單行道 142
12.3 漢諾塔 143
12.4 計算整數分拆 145
12.4.1 計算“1”的個數 147
12.4.2 僅使用奇數進行分拆 148
12.4.3 使用遞增數進行分拆 148
12.4.4 交替使用奇偶數進行分拆 149
12.4.5 整數分拆問題的推廣 151
12.4.6 解決分拆問題的錯誤方法 151
第13章 遞歸函數 152
13.1 在限制條件下選取小球 152
13.2 單行道 155
13.3 漢諾塔 156
13.4 整數分拆 158
13.5 階乘 159
13.6 斐波那契數列 161
13.7 利用gprof進行性能分析 165
第14章 整數分拆 167
14.1 堆內存和棧內存 168
14.2 追蹤遞歸函數調用 176
14.3 約束條件下的分拆 178
14.3.1 僅使用奇數進行分拆 179
14.3.2 使用遞增數進行分拆 179
14.3.3 交替使用奇偶數進行分拆 180
14.3.4 使用gprof和gcov查找性能瓶頸 180
第15章 使用遞歸解決問題 187
15.1 二分搜索 187
15.2 快速排序 189
15.3 排列組合 195