詞法陷阱: 1 = 不同于==不要在程序中將兩者寫錯(cuò),小心。將表達(dá)式與常量比較時(shí),可將常量放在左邊。 2 &和| 不同于&& 和 ||. 3 詞法分析中的貪心法:每個(gè)符號(hào)應(yīng)該包含盡可能多的字符。如果(編譯器的)輸入流截至某個(gè)字符前都已經(jīng)分解為一個(gè)個(gè)符號(hào),那么下一個(gè)符號(hào)將包括從該字符之后可能組成一個(gè)字符的最長(zhǎng)字符串。如y = x/*p,那么/*將作為一個(gè)符號(hào)對(duì)待。 4 如果一個(gè)整形變量第一個(gè)字符是0,那么該常量被視為8進(jìn)制數(shù)。 5 Char c = ‘cxf’。在vc和Gcc中,依次用后一個(gè)字符覆蓋前一個(gè)字符,最后得到的整數(shù)值是最后一個(gè)字符的整數(shù)值。 語法陷阱: 1 c變量聲明由類型和一組類似表達(dá)式的聲明符組成。聲明符與表達(dá)式類似,對(duì)他求值返回一個(gè)聲明中給定類型的結(jié)果。如float f, ((f))。知道了如何聲明一個(gè)變量,那么該類型的類型轉(zhuǎn)換符就很容易得到了:將聲明中的變量名和分號(hào)去掉,再將剩余的部分用個(gè)括號(hào)“封裝”起來即可。如 float(*h) (),則float(*)()就是“指向返回值是浮點(diǎn)類型的函數(shù)的指針”的類型轉(zhuǎn)換符。(*(void(*)())0)()調(diào)用地址為0位置的的例程。 2 運(yùn)算符優(yōu)先級(jí):?jiǎn)文窟\(yùn)算符,算術(shù)運(yùn)算符,移位,關(guān)系,邏輯,條件, 賦值。 3 switch語句中case中,不要忘記break,若刻意要省略,請(qǐng)加注釋。 4 C語言中只有一維數(shù)組,而且數(shù)組的大小必須在編譯期間就作為一個(gè)常數(shù)確定下來。多維數(shù)組是通過一維數(shù)組仿真的,因?yàn)閿?shù)組的元素可以是任何對(duì)象,當(dāng)然也可以是數(shù)組。 對(duì)數(shù)組,我們只能做兩件事,確定其大小,以及獲得指向該數(shù)組下標(biāo)為0的元素的指針。其它的有關(guān)數(shù)組的操作,實(shí)際上是通過指針進(jìn)行的。 語義陷阱: 1 空指針并不等于空字符串。編譯器保證由0轉(zhuǎn)換而來的指針不等于任何有效的指針。當(dāng)將0賦值非一個(gè)指針變量時(shí),絕對(duì)不能企圖使用該指針指向的內(nèi)存中存儲(chǔ)的內(nèi)容。 2 在使用范圍時(shí),使用不對(duì)稱邊界方式。第一個(gè)是“入界點(diǎn)”(序列中第一個(gè)被占用的元素),第二個(gè)是“出界點(diǎn)”(序列中第一個(gè)被釋放的元算)。For(int I = 0 ; I < 10; i++)。盡量不要使用For(int I = 0 ; I <=9; i++)。 3 數(shù)組的下標(biāo)如果用入界口加出界口來表達(dá)(即10個(gè)元素,其下標(biāo)為0 <= n < 10 ),則元素個(gè)數(shù)即為上界與下界之差,即下界。若為空,則上界等于下界。任何情況下上界也永遠(yuǎn)不可能小于下界。 盡量采用非對(duì)稱邊界法。 一個(gè)有N個(gè)元素的數(shù)組 ,我們可以使用a[N]進(jìn)行比較和賦值,但不能引用其內(nèi)容。 4 C語言中只有4個(gè)運(yùn)算符存在規(guī)定的求值順序:&&,| |, ?:和,。其他的運(yùn)算符對(duì)器操作數(shù)求值的順序是未定義的。特別的是,賦值運(yùn)算符并不保證任何求值順序。Y[i]=X[i++] 錯(cuò)誤。 5 記得為main提供返回值。 連接: 1 為避免命名沖突,請(qǐng)對(duì)變量或函數(shù)使用static修飾符。為了定義與庫函數(shù)中同名的函數(shù),可將文件中要定義的函數(shù)加static修飾。 2 使用外部函數(shù)前,一定要聲明。否則,沒有聲明,函數(shù)返回值將默認(rèn)為整型。 3 外部聲明要與定義類型一致。不能聲明是extern int n,而定義是long n. 4 同一個(gè)外部變量在不同的地方被聲明為不同的類型,這種錯(cuò)誤大部分編譯器是檢不出來的。 char file[]= "/etc/password"; 與 extern char* file; 是不一樣的。 庫函數(shù): 1 注意getchar()返回整型,不是字符型。 2 為了保持與過去不能同時(shí)進(jìn)行讀寫操作的程序的向下兼容性,一個(gè)輸入操作不能隨后直接緊跟一個(gè)輸出操作,反之依然,如果要同時(shí)進(jìn)行輸入和輸出操作,必須在其中插入fseek函數(shù)的調(diào)用。例:
3 緩沖輸出和內(nèi)存分配: 可通過setbuf函數(shù)控制程序的緩沖輸出。
這個(gè)是不對(duì)的。buf最后一次被清空是在什么時(shí)候?答案是在main函數(shù)結(jié)束之后,作為程序交回控制給操作系統(tǒng)之前C運(yùn)行時(shí)庫所必須進(jìn)行的清理工作的一部分。但是在此之前buf已經(jīng)被釋放。 解決方法一是加上static 聲明。也可以把buf聲明完全移到main函數(shù)之外。第二種辦法是動(dòng)態(tài)分配緩沖區(qū),在程序中并不主動(dòng)釋放分配的緩沖區(qū) 4 不能直接使用errno檢測(cè)錯(cuò)誤,應(yīng)先檢測(cè)作為錯(cuò)誤指示的返回值,確定程序已經(jīng)執(zhí)行失敗。然后,再檢查errno,搞清原因。 /* 調(diào)用庫函數(shù) */ if(返回的錯(cuò)誤值) 檢查errno 5 庫函數(shù)signal 從理論上說,一個(gè)信號(hào)可能在C程序執(zhí)行期間的任何時(shí)刻上發(fā)生,甚至可能出現(xiàn)在某些復(fù)雜的庫函數(shù)(如malloc)的執(zhí)行過程中。因此從安全的角度講,信號(hào)的處理函數(shù)不應(yīng)該調(diào)用上述類型的庫函數(shù)?;谕瑯拥脑颍瑥膕ignal處理函數(shù)中使用longjump退出,通常情況下也是不安全的:因?yàn)樾盘?hào)可能發(fā)生在malloc 或者其它庫函數(shù)開始更新某個(gè)數(shù)據(jù)結(jié)構(gòu),卻又沒有最后完成的過程中。因此signal處理函數(shù)能夠做的安全的事情,似乎就只有設(shè)置一個(gè)標(biāo)志然后返回,期待以后主程序能夠檢查到這個(gè)標(biāo)志,發(fā)現(xiàn)一個(gè)信號(hào)已經(jīng)發(fā)生。 然而,就算這樣做也并不總是安全的。當(dāng)一個(gè)算術(shù)運(yùn)算錯(cuò)誤引發(fā)一個(gè)信號(hào)時(shí),某些機(jī)器在signal處理函數(shù)返回后還將重新執(zhí)行失敗的操作。因此對(duì)于算術(shù)運(yùn)算 錯(cuò)誤,signal處理函數(shù)的惟一安全、可移植的操作就是打印一條出錯(cuò)消息,然后使用longjump或exit立即退出程序。 當(dāng)一個(gè)程序異常終止時(shí),程序輸出的最后幾行常常會(huì)丟失,原因是緩沖。 預(yù)處理器: 1 不要忽視宏中的括號(hào)。 2 宏不是函數(shù)。將宏中的參數(shù)都加上括號(hào),將整個(gè)結(jié)果表達(dá)式也括起來。防止副作用。 3 宏不是語句:#define assert(e) ((void)((e)||_assert_error(_FILE_,_LINE_))) 4 宏不是類型定義;不要用#define定義類型,而是用typedef定義新類型。 可移植性: 1 因?yàn)樽址A靠梢杂脕肀硎疽粋€(gè)字符數(shù)組,所以在數(shù)組名出現(xiàn)的地方都可以用字符串常量末端替換。 如: "0123456789"[n%10] 2 注意C標(biāo)準(zhǔn)的變化,新特性的使用。 3 c標(biāo)準(zhǔn)所能保證的只是,c實(shí)現(xiàn)必須能夠區(qū)別出前6個(gè)字符不同的外部名稱,且并沒有要求區(qū)分大小寫。避免諸如:print_char(),print_int()等 4 整數(shù)長(zhǎng)度的相對(duì)長(zhǎng)度規(guī)定:short類型的值肯定能被int型容納,int型肯定能被long型整數(shù)容納;一個(gè)普通(int類型)整數(shù)足夠大以容納任何數(shù)組下標(biāo);字符長(zhǎng)度由硬件特性決定。 5 隨機(jī)數(shù)最大值:RAND_MAX |
|