C陷阱和缺陷學習筆記

2021-05-01 02:50:41 字數 3481 閱讀 1146

詞法陷阱:

1        = 不同於==不要在程式中將兩者寫錯,小心。將表示式與常量比較時,可將常量放在左邊。

2        &和| 不同於&& 和 ||.

3        詞法分析中的貪心法:每個符號應該包含盡可能多的字元。如果(編譯器的)輸入流截至某個字元前都已經分解為乙個個符號,那麼下乙個符號將包括從該字元之後可能組成乙個字元的最長字串。如y = x/*p,那麼/*將作為乙個符號對待。

4        如果乙個整形變數第乙個字元是0,那麼該常量被視為8進製數。

5        char c = 『cxf』。在vc和gcc中,依次用後乙個字元覆蓋前乙個字元,最後得到的整數值是最後乙個字元的整數值。

語法陷阱:

1        c變數宣告由型別和一組類似表示式的宣告符組成。宣告符與表示式類似,對他求值返回乙個宣告中給定型別的結果。如float f, ((f))。知道了如何宣告乙個變數,那麼該型別的型別轉換符就很容易得到了:將宣告中的變數名和分號去掉,再將剩餘的部分用個括號「封裝」起來即可。如float(*h) (),則float(*)()就是「指向返回值是浮點型別的函式的指標」的型別轉換符。(*(void(*)())0)()呼叫位址為0位置的的例程。

2        運算子優先順序:單目運算子,算術運算子,移位,關係,邏輯,條件, 賦值。

3        switch語句中case中,不要忘記break,若刻意要省略,**注釋。

4        c語言中只有一維陣列,而且陣列的大小必須在編譯期間就作為乙個常數確定下來。多維陣列是通過一維陣列**的,因為陣列的元素可以是任何物件,當然也可以是陣列。

對陣列,我們只能做兩件事,確定其大小,以及獲得指向該陣列下標為0的元素的指標。其它的有關陣列的操作,實際上是通過指標進行的。

語義陷阱:

1          空指標並不等於空字串。編譯器保證由0轉換而來的指標不等於任何有效的指標。當將0賦值非乙個指標變數時,絕對不能企圖使用該指標指向的記憶體中儲存的內容。

2          在使用範圍時,使用不對稱邊界方式。第乙個是「入界點」(序列中第乙個被占用的元素),第二個是「出界點」(序列中第乙個被釋放的元算)。for(int i = 0 ; i < 10; i++)。盡量不要使用for(int i = 0 ; i <=9; i++)。

3          陣列的下標如果用入界口加出界口來表達(即10個元素,其下標為0 <= n < 10 ),則元素個數即為上界與下界之差,即下界。若為空,則上界等於下界。任何情況下上界也永遠不可能小於下界。

盡量採用非對稱邊界法。

乙個有n個元素的陣列 ,我們可以使用a[n]進行比較和賦值,但不能引用其內容。

4          c語言中只有4個運算子存在規定的求值順序:&&,| |, ?:和,。其他的運算子對器運算元求值的順序是未定義的。特別的是,賦值運算子並不保證任何求值順序。y[i]=x[i++] 錯誤。

5          記得為main提供返回值。

連線:1          為避免命名衝突,請對變數或函式使用static修飾符。為了定義與庫函式中同名的函式,可將檔案中要定義的函式加static修飾。

2          使用外部函式前,一定要宣告。否則,沒有宣告,函式返回值將預設為整型。

3          外部宣告要與定義型別一致。不能宣告是extern int n,而定義是long n.

4          同乙個外部變數在不同的地方被宣告為不同的型別,這種錯誤大部分編譯器是檢不出來的。

char file= "/etc/password";

與extern char* file;

是不一樣的。

庫函式:

1          注意getchar()返回整型,不是字元型。

2          為了保持與過去不能同時進行讀寫操作的程式的向下相容性,乙個輸入操作不能隨後直接緊跟乙個輸出操作,反之依然,如果要同時進行輸入和輸出操作,必須在其中插入fseek函式的呼叫。例:

file *fp;

struct record rec;

while (fread((char *)&rec, sizeof(rec),1,fp) = 1)}

3         緩衝輸出和記憶體分配:

可通過setbuf函式控制程式的緩衝輸出。

#include

void main(void)

這個是不對的。buf最後一次被清空是在什麼時候?答案是在main函式結束之後,作為程式交回控制給作業系統之前c執行時庫所必須進行的清理工作的一部分。但是在此之前buf已經被釋放。

解決方法一是加上static 宣告。也可以把buf宣告完全移到main函式之外。第二種辦法是動態分配緩衝區,在程式中並不主動釋放分配的緩衝區

4         不能直接使用errno檢測錯誤,應先檢測作為錯誤指示的返回值,確定程式已經執行失敗。然後,再檢查errno,搞清原因。

/* 呼叫庫函式 */

if(返回的錯誤值)

檢查errno

5         庫函式signal

從理論上說,乙個訊號可能在c程式執行期間的任何時刻上發生,甚至可能出現在某些複雜的庫函式(如malloc)的執行過程中。因此從安全的角度講,訊號的處理函式不應該呼叫上述型別的庫函式。基於同樣的原因,從signal處理函式中使用longjump退出,通常情況下也是不安全的:因為訊號可能發生在malloc 或者其它庫函式開始更新某個資料結構,卻又沒有最後完成的過程中。因此signal處理函式能夠做的安全的事情,似乎就只有設定乙個標誌然後返回,期待以後主程式能夠檢查到這個標誌,發現乙個訊號已經發生。

然而,就算這樣做也並不總是安全的。當乙個算術運算錯誤引發乙個訊號時,某些機器在signal處理函式返回後還將重新執行失敗的操作。因此對於算術運算錯誤,signal處理函式的惟一安全、可移植的操作就是列印一條出錯訊息,然後使用longjump或exit立即退出程式。

當乙個程式異常終止時,程式輸出的最後幾行常常會丟失,原因是緩衝。

預處理器:

1         不要忽視巨集中的括號。

2         巨集不是函式。將巨集中的引數都加上括號,將整個結果表示式也括起來。防止***。

3         巨集不是語句:#define assert(e) ((void)((e)||_assert_error(_file_,_line_)))

4         巨集不是型別定義;不要用#define定義型別,而是用typedef定義新型別。

可移植性:

1   因為字串常量可以用來表示乙個字元陣列,所以在陣列名出現的地方都可以用字串常量末端替換。     如:    "0123456789"[n%10]

2  注意c標準的變化,新特性的使用。

3   c標準所能保證的只是,c實現必須能夠區別出前6個字元不同的外部名稱,且並沒有要求區分大小寫。避免諸如:print_char(),print_int()等

4  整數長度的相對長度規定:short型別的值肯定能被int型容納,int型肯定能被long型整數容納;乙個普通(int型別)整數足夠大以容納任何陣列下標;字元長度由硬體特性決定。

5   隨機數最大值:rand_max

C陷阱與缺陷(學習筆記)

掌握細節並不難,難的是如何運用之妙!詞 單詞 符號 作為賦值運算,是因為操作頻繁,書寫簡單 a b與表示式a b的含義相同,而與a b的含義不同 y x p與y x p不同 第乙個 被理解為注釋符 理解 這也許就是編碼規範要求操作符兩側新增合理空格的原因之一吧 用雙引號引起的字串,代表的卻是乙個指向...

《C陷阱與缺陷》學習筆記(二)

第四章 連線 聯結器並不理解c語言,然而它能理解機器語言和記憶體布局。作者強調聯結器並不能處理連線時和c語言相關的一些錯誤,如果c語言提供了lint,要善加利用。每個外部物件都必須在程式某個地方進行定義。這就意味著如果乙個程式中包括了語句extern int a 就應該在別的某個地方包括語句int ...

《C陷阱與缺陷》學習筆記(二)

分類 c 專區 mfc客戶端 2014 06 06 16 21 83人閱讀收藏 舉報 第四章 連線 聯結器並不理解c語言,然而它能理解機器語言和記憶體布局。作者強調聯結器並不能處理連線時和c語言相關的一些錯誤,如果c語言提供了lint,要善加利用。每個外部物件都必須在程式某個地方進行定義。這就意味著...