標籤:讀書筆記 c語言 移植 指標 對象
1. = 不同於 ==
2. & 和 | 不同於 && 和 ||
3. 編譯器將程式分解成符號的方法是,從左至右一個一個讀,直到讀入的字元組成的字串已不在可能組成一個有意義的符號,貪心的處理常式。如a --- b編譯器就會認為是 a-- - b,而不是認為a - --b。
4. 整數常量,如果一個整型常量第一個字元是0,那麼該變數會被認為是8進位數。
5. 單引號引起的一個字元實際上代表一個整數,‘a’與0141和97是嚴格一致的。雙引號引起的字元創代表的是一個指向無名數組其實字元的指標,該數組被雙引號之間的字元以及一個額外的‘\0’初始化。一個代表整數一個代表指標,所以混用編譯器會報錯。
6. 任何C變數的聲明都由兩部分組成,類型以及一組類似運算式的聲明符。
7. 運算子優先順序問題,解決辦法就是加括弧。但最好是記住c語言中運算子的優先順序。
(1) 任何一個邏輯運算子優先順序均低於一個關係運算子。
(2) 移位元運算符的優先順序比算術運算子優先順序要低,但比關係運演算法優先順序要高。
8. 注意作為語句結束符的分號 ;
9. Switch語句,case後一定要寫break; 否則它會一直執行下去。
10. 調用函數的時候,即使沒有參數後面也要有括弧。如f();
11. Else語句始終與同一括弧內最近未匹配的if相結合。所以如果if和else使用的時候要注意添加大括弧。
12. 指標與數組,C語言中只有一位元組,而且數組的大小必須在編譯器就作為一個常數確定下來。我們只能做兩件事,確定該數組的大小和獲得指向該數組下標為0的元素的指標。任何一個數組下標的運算都等同於一個對應的指標運算。
Calendar[month][day] = 0;
*(*(Calendar + month) + day) = 0;
//上述二者其實是等價的
int month;
for (month = 0 ; month < 12 ; month ++)
{
int day;
for (day = 0 ; day < 31 ; day ++)
{
calendar[month][day] = 0;
}
}
int (*monthp)[31];
for (monthp = calendar ; monthp < &calendar[12] ; monthp ++)
{
int *dayp;
for (dayp = *monthp ; dayp < &(*monthp)[31] ; dayp ++)
{
*dayp = 0;
}
}
//上述二者其實是等價的
13. 非數組指標
//將s和t兩個字串合并為一個字串r
char *r,*malloc();
r = malloc(strlen(s) + strlen(t) + 1);
if(!r){complain();exit(1);}
strcpy(r , s);
strcat(r , t);
……//一段時間之後進行釋放
free(r);
14. 作為參數的數組聲明,實際上數組名會被轉換成指向該數組的第一個元素的指標進行傳遞。
15. 當指標相互賦值的時候,實際上傳遞的是這個數組的首地址,也就是說兩個指標指向記憶體中同一地址。改變其中一個指標時另外一個也會改變,因為他們是指向相同地址。
16. null 指標並非Null 字元串
17. 仔細計算邊界,C語言中數組的下標是從0開始計算。用第一個入界點和第一個出界點來表示一個數值範圍。
18. 求值順序,C語言只有四個運算子(&&、||、?: 、,)存在規定的求值順序,運算子&&和||首先對左側運算元求值,只有需要時才對右側運算元求值。運算子a?b:c中運算元a首先被求值,根據a的值再求運算元b或者c的值。而逗號運算子首先對左側運算元求值,然後該值被丟棄再對右側運算元求值。
19. 運算子&&、||、和!
對運算元的處理方式是將其視作要麼為真要麼為假,通常視0為假,而非0為真。
運算子&、| 和 ~
對運算元的處理方式是將其視為一個二進位的位序列,分別對其每個位進行操作。
20. 整數溢出,在無符號數中是沒有溢出的概念的,只有兩個運算元都為有符號整數時,溢出才有可能發生。可以將其都轉換為無符號數來運算。
21. 為函數main提供返回值,返回值為0則代表程式運行成功,返回值為非0則表示程式執行失敗。
22. 串連
一個C程式可能有多個分別編譯的部分組成,這些不同的部分通過連機器的程式組成一個整體。
Lint這個程式就是檢查串連過程中錯誤的程式應該重視,並且善加利用。
23. C語言中一個重要思想就是分別編譯,即若干的來源程式可以在不同的時候單獨進行編譯,然後在恰當的時候整合在一起。編譯器會產生若干的目標模組,連接器通常將目標模組看成一組外部對象組成的。每個外部對象代表著及其記憶體中的某個部分,並通過一個外部名稱來識別,連接器的輸入是一組目標模組和庫檔案,連接器的輸出是一個載入模組,因為連接器對C語言知之甚少,所以有很多錯誤不能被檢測出來,lint程式一定要使用。
24. 聲明和定義
int a;//定義a的變數
extern int a;//實際上是一個聲明,對外部變數a的引用
每個外部變數最只定義一次,因為定義多次引用會出錯。
25. 命名衝突與static修飾符
static int a;將a的範圍限制在一個源檔案中,對於其他源檔案來說a是不可見的。因此如果若干個函數需要共用一組外部對象,可以將這些函數放在一個源檔案中,把它們需要用到的對象也都在一個源檔案中以static修飾符聲明。同樣static不僅適用於變數,也適用於函數。有了static之後就不用擔心多個源檔案中有相同函數名了。
26. 形參、實參與返回值
任何函數都有一個形參列表,列表中的每一個參數都是一個變數,該變數調用過程中被初始化。對於某些函數來說形參列表為空白,函數調用時將實參列表傳遞給被調用的函數。
如果一個函數在被定義和聲明之前被調用,那麼它的傳回型別預設為整型。
27. 檢查外部類型
假設我們有一個C程式,它由兩個源檔案組成,一個檔案中包含外部變數n的聲明 extern int n;另一個檔案中包含 long n;這是一個無效的程式,但是有的C編譯器可能檢測不出這種問題。
28. 標頭檔
每個外部對象只在一個地方聲明,這個聲明的地方一般就在一個標頭檔中,需要用到該外部對象的所有模組都應該包含這個標頭檔,一定要注意定義該外部對象的模組也應該包含這個標頭檔。
29. 返回整數的getchar函數
一般情況下返回的是標準輸入檔案中的下一個字元,無輸入時返回EOF。
30. 讀寫操作不能同時
如果想同時讀寫,那麼必須在其中插入fseek函數的調用。
fseek(檔案指標,從起始位置跳過多少位元組,起始位置)
31. 緩衝輸出與記憶體配置
程式的輸出有兩種方式,一種是即時處理,另一種是先暫存起來,然後再大塊寫入。
setbuf(stdout,buf);語句將通知輸入/輸出庫,所有寫入到stdout的輸出都應該使用buf作為輸出緩衝區,直到緩衝區滿或者調用fflush的時候才將檔案實際寫入stdout中。緩衝區的大小有標頭檔stdio.h中的BUFSIZ定義。
將buf數組定義為靜態,或者放在main函數之外,再或者使用動態分配setbuf(stdout,malloc()); main函數結束之後,C執行階段程式庫會進行清理工作,如果設定為普通數組,main結束之後就被釋放了,當執行階段程式庫再次釋放的時候就會報錯。
32. 庫函數signal
作為捕捉非同步事件的一種方式,要是用該庫函數需要在源檔案中加上#include<signal.h>要處理一個特定的訊號,可以這樣調用
signal(signal type,handler function);
signal type代表要捕捉的訊號類型
handler function當事件發生時,將要加以調用的事件處理函數
訊號非常棘手,盡量讓處理函數簡單,我們可以很容易修改。
33. 宏定義中的空格
#define f (x) ((x)-1)代表的意思就是,f等價於(x) ((x)-1),所以空格神馬的一定要注意,這一點不適用於宏調用,只對宏定義適用。
正確的定義方式#define f(x) ((x)-1)
34. 宏不是函數
宏定義中的括弧它們的作用是預防引起與優先順序有關的問題。
35. 宏不是語句
考慮一下assert宏,它的參數是一個運算式,如果該運算式為0,則使程式終止執行,並給出一條出錯資訊。定義之後可以將其還原帶程式中,看其是否有問題。
36. 宏並不是類型定義
#define FOOTYPE struct foo
FOOTYPE a;
FOOTYPE b,c;
這樣的用法有一個優點就是可移植性,但最好還是使用類型定義
typedef struct foo FOOTYPE;
37. 字元是有符號數還是無符號數
使用語句(unsigned char)c,一個unsigned char類型的字元可以直接轉換為無符號數。
38. 移位元運算符
如果被移位的對象長度是n位,那麼移位計數必須大於或等於0,而嚴格小於n。因此不肯那個做到在單次操作中將某個數值的所有位都移出,加上這個限制條件,我們就能在硬體上更高效的實現移位元運算。
39. 記憶體位置0
null指標並不指向任何對象,因為有的機器不允許讀記憶體為0的地方,而有的機器可以讀記憶體為0的地方,所以就導致程式移植到另一台機器就會出現問題。
40. 首先釋放,然後重新分配
大多數C語言實現都為使用者提供了3個記憶體配置函數:malloc、realloc、free。調用malloc(n)將返回一個指標,指向一個新分配的可以容納n個字串的記憶體,將malloc返回的指標作為參數傳入free的時候,就釋放了這塊記憶體。調用realloc函數的時候,需要把指向已指派記憶體指標以及這塊記憶體新的大小作為參數傳入,就可以調整這塊新地區的記憶體大小了。
《c陷阱與缺陷》讀書筆記