[書評] Expert C Progranmming: Deep C Secrets
Chapter 1. C Through the Mists of Time
這個章節分析 C 語言的歷史, 以及 C 的標準
ANSI C, ISO C, 還有當初在創造 C 語言時, 做了哪些選擇
這章可以當故事書看, 我相信讀歷史有幫助瞭解事物背後的原因 :)
作者建議可以讀讀 C standard, 我想還是算了 XD
Chapter 2. It's Not a Bug, It's a Language Feature
用 switch 語法 "一路跑到底" 沒有自動 break 講起
C 語言為了簡潔, 賦予關鍵字太多意義, 例如 static 在函數裡外的意義就不同
另一個值得一提的是 C 的 operator, 有 *, [], (), ->, &&, ||, &, |, ==
這一大堆拼湊在一起, 誰先誰後經常是 bug 的溫床
這章也有 buffer overflow 的具體說明, 值得一看
這也是 windows 系統漏洞的溫床之一
Chapter 3. Unscrambling Declarations in C
C 語言裡有些定義很古怪, 比如定義指標
int *a;
為什麼不寫成
int &a;
意義還比較清楚, 答案在這一章有給出
"there is the C philosophy that the declaration of an object should
look like its use"
於是真相大白了, 原來是 C的設計哲學
後來的 C++ 用了 & 代表 reference, 算是比較進步的作法吧
另外 C 語言裡, 最恐怖的東西莫過於 function pointer 的定義...
這是從 "telnet" 程式碼摘錄出來的 prototype:
char * const * (*next)();
或是這個 UNIX 的 signal() 的 prototype:
void (*signal(int sig, void (*func)(int)) ) (int);
這是什麼鬼 !! 翻遍市面上所有講 C 的書, 恐怕少有能講清楚的了
但是這本書做到了, 這一章值得一看, 看完保證對 C 有另一層看法
另外這一章還針對 typedef, #define 的差異做了說明:
#define INT_PTR int*
typedef int* int_ptr;
INT_PTR a, b; -> int *a, b -> a is pointer, b is variable
int_ptr a, b; -> a & b are pointer
精闢啊 !!
Chapter 4. The Shocking Truth: C arrays and Pointers Are NOT the Same
這章節在講 array 和 pointer 是不同的東西
會有這樣的 issue, 來自於陣列的名稱, 本身可以當指標用
但是理解了背後的實作以後, 自然不會搞混
這一章不長, 但是我想到的是計算機組織 (微算機系統) 這門課
課程裡, 再再舉了 C 語言的例子, 然後示範怎麼翻譯成組合語言
如果學了 C, 再仔細思考這些程式片段, 對程式的功力是大有幫助的
比如陣列的 address 通常會先放在一個 register 裡, 比如叫 $R1
然後會再有個 register 放陣列的 offset, $R2吧
取出的結果放在$R3
ldr $R3, $R1[$R2]
這是存取陣列的基礎, C 存取陣列的語法, 其實跟 machine code 貼得很近
另外是 struct 的語法, 要怎麼去存取每個 member 其實是 ldr 這類的指令
只是這時候, offset 變成相對 struct 起點的 offset
要深入瞭解 C 語言, 對組合語言有一定的認識是有必要的
畢竟瞭解事物背後的原理, 才能掌握一切的規律
Chapter 5. Libraries, Linking, and Loading
不知道有沒有人想過, printf() 的實作到底是在哪裡
我先說答案, 絕對不是在 stdio.h 裡面 XD
如果在 UNIX 上, 這個 library 會放在
/lib/libc.so.6
如果是 windows, 他會放在
C:\Program Files\Microsoft Visual Studio 9.0\VC\lib\msvcrt.lib
或著微軟也有給了說明 http://support.microsoft.com/kb/94248
總之, library就是一堆人家寫好的 function, 要用別人工作的結果
必須把自己的程式和別人的相連, 具體的行為, 就是靠著 linker 來做
----
我很少有看書在提 linker 動作的細節, 包括 static link 以及 dynamic link
以及這些 linker 會遇到的 inter-positioning 的問題
作者建議至少要看看 linker 的輸出, 瞭解一下到底 linker 做了些什麼
Chapter 6. Poetry in Motion: Runtime Data Structures
* a.out
為什麼 UNIX 裡執行檔預設的名字是 a.out
原來這代表的是 "assembler output", 不過其實他是 linker output
真正的 a.out, 是 PDP-7 這台機器才有, 上面沒有 linker
* Segments
執行檔, 不管在 windows/ UNIX 都由多個 segments 構成
所謂 data/ code/ bss/ data segments 是也
了解執行檔的結構, 對了解 C 語言也有幫助, 比如宣告一個 global 陣列...
int arr1[100];
這個陣列會被放在 BSS segments, 不占執行檔空間, 程式載入時, 會被清成 0
如果是這樣宣告...
int arr2[3] = {1, 2, 3};
那這個陣列會放在 DATA segments, 因為他有初始值, 會占執行檔空間
* Stack/Heap, Typical memory mapping
了解程式執行的記憶體是很重要的, 舉例來講, addreess 0 的地方通常不拿來用
配上 MMU (virtual memory) 一起使用, 如果程式試圖存取這個地方發生 page fault
就能揪出一些錯誤的指標存取
另外 Stack 和 Heap 長的方向, 以及配置在記憶體的位置也很重要
很多人都有經驗, 在 function 裡想開大陣列, 但是沒辦法開
原因就是 function 裡的陣列是放在 stack 裡, 如果 stack 不夠大, 當然放不下
有個解法是: 把這個陣列宣告成 global, 放在 BSS segment, 就可以解這個問題
另一個解法是: 把 stack 宣告得更大
在寫 Embedded System 程式時, 甚至整個 memory map 都要手動配置
了解這些看似沒有用處的細節, 其實是成為高手的重要關鍵
* What Happens When a Function Gets Called: The Procedure Activation Record
要把 C 學得紮實, 一定要從組語的角度思考問題, Think in Assembly Level
呼叫函數時, 變數是怎麼傳遞, 哪些東西被塞到 stack 裡
這些是呼叫函數的開銷, 這些開銷和函數的本體相比, 決定用不用 inline 語法
另外 Visual C++ 能在 debug 時看到 call trace , 看得到函數呼叫的流程
這個章節說明確切的實作方法, 值得一看
* setjmp/longjmp
這個功能是 C 的高階功能, 他有一些特別的用途, 值得一看
之後 C++ 的 try / cache 聽說是用類似的東西實作的
Chapter 7. Thanks for the Memory
這一章在講記憶體的故事, 任何一個熱愛電腦的人
都應該讀讀這一章, 看看 80x86 address mode 怎麼演變
有很多小時候不清楚的事 (eg. 640KB 限制怎麼來的), 這兒都找得到答案
懷古之後, Virtual Memory 的觀念登場了
這本書沒提到 VM 應當怎麼實做, 想看精彩的 VM 說明, 白算盤可能適當些
這章偏重觀念上的講解, 對 Memory Hierachy 不大懂得人可以看看
但是最精彩的 Memory Hierachy, 我認為要從計算機組織出發
這樣的觀點才是最完整的
Chapter 9. More About Arrays
Chapter 10. More About Pointers
Array 和 Pointer 有相當多相似之處, 這章針對定義和使用方式再說明
Multi-dimensional Arrays 實際在記憶體的排列方法也值得一看
如何傳遞 multi-dimension 陣列到函數也很重要, 舉例來說
int a[100][30][10];
要怎麼傳遞給任意函數? 答案是下面的 prototype:
fun(int a[][30][10])
陣列最尾巴的 dimension 必須要指定, 最開始的不用
如果你沒辦法回答為什麼會這樣, 那這兩章值得一讀, 這和陣列存取的方式有關系
Chapter 11. You Know C, So C++ is Easy
這章以 C 的觀點, 介紹 C++ 的性質, 可以當作一篇簡單的 C++ tutorial
C++ 是一個博大精深的語言, 要精通全部的 C++ 真的很困難, 而且沒必要
我相信80/20法則在這裡依舊是用: 80% 的工作, 只用 20% 的工具完成
----
Appendix: Silicon Valley Programmer Interviews
這篇附錄是整本書我最喜歡的章節之一, 這裡有真正的矽谷工程師面試實況
讀起來趣味橫生, 又不失內容
----
這本書的心得到此為止, 讀過的人, 也歡迎來我的版貼上你的 note :)
這個章節分析 C 語言的歷史, 以及 C 的標準
ANSI C, ISO C, 還有當初在創造 C 語言時, 做了哪些選擇
這章可以當故事書看, 我相信讀歷史有幫助瞭解事物背後的原因 :)
作者建議可以讀讀 C standard, 我想還是算了 XD
Chapter 2. It's Not a Bug, It's a Language Feature
用 switch 語法 "一路跑到底" 沒有自動 break 講起
C 語言為了簡潔, 賦予關鍵字太多意義, 例如 static 在函數裡外的意義就不同
另一個值得一提的是 C 的 operator, 有 *, [], (), ->, &&, ||, &, |, ==
這一大堆拼湊在一起, 誰先誰後經常是 bug 的溫床
這章也有 buffer overflow 的具體說明, 值得一看
這也是 windows 系統漏洞的溫床之一
Chapter 3. Unscrambling Declarations in C
C 語言裡有些定義很古怪, 比如定義指標
int *a;
為什麼不寫成
int &a;
意義還比較清楚, 答案在這一章有給出
"there is the C philosophy that the declaration of an object should
look like its use"
於是真相大白了, 原來是 C的設計哲學
後來的 C++ 用了 & 代表 reference, 算是比較進步的作法吧
另外 C 語言裡, 最恐怖的東西莫過於 function pointer 的定義...
這是從 "telnet" 程式碼摘錄出來的 prototype:
char * const * (*next)();
或是這個 UNIX 的 signal() 的 prototype:
void (*signal(int sig, void (*func)(int)) ) (int);
這是什麼鬼 !! 翻遍市面上所有講 C 的書, 恐怕少有能講清楚的了
但是這本書做到了, 這一章值得一看, 看完保證對 C 有另一層看法
另外這一章還針對 typedef, #define 的差異做了說明:
#define INT_PTR int*
typedef int* int_ptr;
INT_PTR a, b; -> int *a, b -> a is pointer, b is variable
int_ptr a, b; -> a & b are pointer
精闢啊 !!
Chapter 4. The Shocking Truth: C arrays and Pointers Are NOT the Same
這章節在講 array 和 pointer 是不同的東西
會有這樣的 issue, 來自於陣列的名稱, 本身可以當指標用
但是理解了背後的實作以後, 自然不會搞混
這一章不長, 但是我想到的是計算機組織 (微算機系統) 這門課
課程裡, 再再舉了 C 語言的例子, 然後示範怎麼翻譯成組合語言
如果學了 C, 再仔細思考這些程式片段, 對程式的功力是大有幫助的
比如陣列的 address 通常會先放在一個 register 裡, 比如叫 $R1
然後會再有個 register 放陣列的 offset, $R2吧
取出的結果放在$R3
ldr $R3, $R1[$R2]
這是存取陣列的基礎, C 存取陣列的語法, 其實跟 machine code 貼得很近
另外是 struct 的語法, 要怎麼去存取每個 member 其實是 ldr 這類的指令
只是這時候, offset 變成相對 struct 起點的 offset
要深入瞭解 C 語言, 對組合語言有一定的認識是有必要的
畢竟瞭解事物背後的原理, 才能掌握一切的規律
Chapter 5. Libraries, Linking, and Loading
不知道有沒有人想過, printf() 的實作到底是在哪裡
我先說答案, 絕對不是在 stdio.h 裡面 XD
如果在 UNIX 上, 這個 library 會放在
/lib/libc.so.6
如果是 windows, 他會放在
C:\Program Files\Microsoft Visual Studio 9.0\VC\lib\msvcrt.lib
或著微軟也有給了說明 http://support.microsoft.com/kb/94248
總之, library就是一堆人家寫好的 function, 要用別人工作的結果
必須把自己的程式和別人的相連, 具體的行為, 就是靠著 linker 來做
----
我很少有看書在提 linker 動作的細節, 包括 static link 以及 dynamic link
以及這些 linker 會遇到的 inter-positioning 的問題
作者建議至少要看看 linker 的輸出, 瞭解一下到底 linker 做了些什麼
Chapter 6. Poetry in Motion: Runtime Data Structures
* a.out
為什麼 UNIX 裡執行檔預設的名字是 a.out
原來這代表的是 "assembler output", 不過其實他是 linker output
真正的 a.out, 是 PDP-7 這台機器才有, 上面沒有 linker
* Segments
執行檔, 不管在 windows/ UNIX 都由多個 segments 構成
所謂 data/ code/ bss/ data segments 是也
了解執行檔的結構, 對了解 C 語言也有幫助, 比如宣告一個 global 陣列...
int arr1[100];
這個陣列會被放在 BSS segments, 不占執行檔空間, 程式載入時, 會被清成 0
如果是這樣宣告...
int arr2[3] = {1, 2, 3};
那這個陣列會放在 DATA segments, 因為他有初始值, 會占執行檔空間
* Stack/Heap, Typical memory mapping
了解程式執行的記憶體是很重要的, 舉例來講, addreess 0 的地方通常不拿來用
配上 MMU (virtual memory) 一起使用, 如果程式試圖存取這個地方發生 page fault
就能揪出一些錯誤的指標存取
另外 Stack 和 Heap 長的方向, 以及配置在記憶體的位置也很重要
很多人都有經驗, 在 function 裡想開大陣列, 但是沒辦法開
原因就是 function 裡的陣列是放在 stack 裡, 如果 stack 不夠大, 當然放不下
有個解法是: 把這個陣列宣告成 global, 放在 BSS segment, 就可以解這個問題
另一個解法是: 把 stack 宣告得更大
在寫 Embedded System 程式時, 甚至整個 memory map 都要手動配置
了解這些看似沒有用處的細節, 其實是成為高手的重要關鍵
* What Happens When a Function Gets Called: The Procedure Activation Record
要把 C 學得紮實, 一定要從組語的角度思考問題, Think in Assembly Level
呼叫函數時, 變數是怎麼傳遞, 哪些東西被塞到 stack 裡
這些是呼叫函數的開銷, 這些開銷和函數的本體相比, 決定用不用 inline 語法
另外 Visual C++ 能在 debug 時看到 call trace , 看得到函數呼叫的流程
這個章節說明確切的實作方法, 值得一看
* setjmp/longjmp
這個功能是 C 的高階功能, 他有一些特別的用途, 值得一看
之後 C++ 的 try / cache 聽說是用類似的東西實作的
Chapter 7. Thanks for the Memory
這一章在講記憶體的故事, 任何一個熱愛電腦的人
都應該讀讀這一章, 看看 80x86 address mode 怎麼演變
有很多小時候不清楚的事 (eg. 640KB 限制怎麼來的), 這兒都找得到答案
懷古之後, Virtual Memory 的觀念登場了
這本書沒提到 VM 應當怎麼實做, 想看精彩的 VM 說明, 白算盤可能適當些
這章偏重觀念上的講解, 對 Memory Hierachy 不大懂得人可以看看
但是最精彩的 Memory Hierachy, 我認為要從計算機組織出發
這樣的觀點才是最完整的
Chapter 9. More About Arrays
Chapter 10. More About Pointers
Array 和 Pointer 有相當多相似之處, 這章針對定義和使用方式再說明
Multi-dimensional Arrays 實際在記憶體的排列方法也值得一看
如何傳遞 multi-dimension 陣列到函數也很重要, 舉例來說
int a[100][30][10];
要怎麼傳遞給任意函數? 答案是下面的 prototype:
fun(int a[][30][10])
陣列最尾巴的 dimension 必須要指定, 最開始的不用
如果你沒辦法回答為什麼會這樣, 那這兩章值得一讀, 這和陣列存取的方式有關系
Chapter 11. You Know C, So C++ is Easy
這章以 C 的觀點, 介紹 C++ 的性質, 可以當作一篇簡單的 C++ tutorial
C++ 是一個博大精深的語言, 要精通全部的 C++ 真的很困難, 而且沒必要
我相信80/20法則在這裡依舊是用: 80% 的工作, 只用 20% 的工具完成
----
Appendix: Silicon Valley Programmer Interviews
這篇附錄是整本書我最喜歡的章節之一, 這裡有真正的矽谷工程師面試實況
讀起來趣味橫生, 又不失內容
----
這本書的心得到此為止, 讀過的人, 也歡迎來我的版貼上你的 note :)
留言