閱讀《C陷阱與缺陷》的知識增量
阿新 • • 發佈:2019-04-07
分配 之前 mon 釋放 title 無符號數 clean 改變 clear
版權聲明:本文為Focustc原創文章。轉載請註明作者及出處。 https://blog.csdn.net/caozhankui/article/details/35925939
轉載請註明作者:Focustc,博客地址為http://blog.csdn.net/caozhk,原文鏈接為點擊打開
看完《C陷阱與缺陷》,忍不住要又一次翻一下,記錄一下與自己的慣性思維不符合的地方。
記錄的是知識的增量。是這幾天的流量,而不是存量。
這本書是在ASCI C/C89訂制之前寫的。有些地方有疏漏。
第一章 詞法陷阱
- 1.3 C語言中解析符號時使用貪心策略,如
x+++++y
將被解析為x++ ++ +y
。並編譯出錯。 - 1.5 單引號引起的一個字符代表一個相應的整數,對於採用ASCII字符集的編譯器而言,‘a‘與0141、97含義一致。
- 練習1.1 嵌套凝視(如
/*/**/*/
)僅僅在某些C編譯器中同意,如gcc4.8.2編譯時是不支持的。
第二章 語法陷阱
- 2.6 else始終與同一個括號內近期的未匹配的if結合
第三章 語義陷阱
- 3.1?
int a[12][31]
表示的是一個長度12的數組,每一個元素是一個長度31的數組。 - 3.1 在須要指針的地方假設使用數組名來替換,那麽數組名就被視為其下標為0的元素的指針,
p = &a
- 3.2 怎樣連接兩個給出的字符串s、t?細節非常重要,書中給出的答案例如以下:
char *r,*malloc()
//原文稱不能直接聲明一個s、t長度之和的數組,但c99能夠聲明變長數組,已經能夠了
//記得要把長度加1
r = malloc(strlen(s) + strlen(t) +1);
//必須推斷內存是否分配成功
if(!r){
complain();
exit(1);
}
strcpy(r,s);
strcat(r,t);
......
//完畢之後一定要釋放r
free(r);
- 3.6 怎樣正確計算數組的邊界?原則一。考慮最簡單情況下的特例;原則二,細致計算邊界。
- 3.6 下面一段代碼為何引起死循環?這是由於在內存地址遞減時,a[10]就是i。
int i,a[10]; for(i = 1; i<=10; i++) a[i] = 0;
- 3.6 邊界的編程技巧:用第一個入界點和第一個出界點表示數值範圍,即[low,high)。
這種效果是
- 取值範圍的大小為兩者之差。
- 若取值範圍為空,則上界等於下界。
- 3.6?
--n
一般比n--
執行速度更快。 - 3.7 運算符&&和||保證兩個操作數從左至右求值。其它運算符的操作數求值順序沒有定義。
比方
y[i] = x[i++]
結果是沒有定義的。 - 3.9 怎樣檢測a+b是否溢出?
if(a+b < 0)
是不對的,由於溢出時的行為是沒有定義的。正確的方法是將兩者轉換為unsigned型與INT_MAX比較- 更巧妙的方法:
if(a > INT_MAX - b)
第四章 連接
- 4.2?
int a
若出如今全部函數體之外,則完畢了聲明與定義(分配存儲空間)。而
extern int a;
僅僅是聲明。說明a的存儲空間是在其它地方分配的,不是定義;因此必須在別的某個地方定義。同一個或不同的源文件均可。 - 4.3 static修飾符能夠將一個函數或變量的作用域限制在一個源文件之內。不會與其它文件裏的同名量發生沖突
- 4.5 聲明與定義必須嚴格同樣。而數組和指針是不同的。
- 4.6 怎樣避免聲明與定義不符?遵守“每一個外部對象僅僅在一個地方聲明”的規則就可以。一般放在頭文件裏,全部用到此外部對象的源文件都要包含此頭文件,定義此對象的文件也應該包含此頭文件。
第五章 庫函數
- 5.1 getchar()返回整數,不能把返回值賦值給char型變量再與EOF比較,由於EOF定義為-1,應該賦值給int型變量。
- 5.2 假設要對文件進行連續的read和write操作,則中間必須插入fseek函數調用。
- 5.3?
setbuf(stdout, buf);
能夠強制將buf指向的char數組設為緩沖區,改變輸出緩存大小。 - 5.3 書中使用緩沖區把stdin的內容拷貝到stdout的程序是錯誤的,由於緩沖區內容的寫出直到緩沖區滿或調用fflush才開始完畢。
能夠把buf聲明為靜態的或者malloc在堆中,防止main函數結束後buf清空。
- 5..1 一個程序異常終止時,程序輸出的最後一部分經常丟失,能夠使用setbuf指向一個空指針作為緩沖區
- 5..2 putchar/getchar在stdio.h中使用宏實現,假設沒有包含stdio.h。非常大可能仍能執行,可是使用相應的函數取代,速度減少。
第六章 預處理器
- 6 宏僅僅是對文本處理,是一個表達式,不是函數或語句
- 6.1 宏定義最好把每一個參數和整個表達式使用括號括起來防止出錯。
- 6.2 假設一個操作數在兩個地方用到。將被求值兩次。解決方式:操作數應該沒有副作用;將宏實現為函數。
- 6.2 宏可能產生非常龐大的表達式。
- 6.3 宏的分號的使用非常麻煩,assert的一種正確實現:
#define assert(e) ((void)((e)||_assert_error(__FILE__,__LINE__)))
- 6.4?
typedef struct foo FOOTYPE
是類型定義語句,定義了一個新的類型。
第七章 可移植性缺陷
- 7.4 編譯器實現可能將字符當作有符號或無符號的。
char轉換為int時結果沒有定義。能夠使用unsigned char避免。
- 7.4 將字符變量轉換為無符號整數時應該使用
(unsigned char)c
而不是(unsigned)c
,後者將c轉換為int再轉換為unsigned int。 - 7.5 除法運算速度大大慢於移位。
- 7.7 整數除法運算時。僅規定
商 x 除數 + 余數 == 被除數
。大多數實如今負數的除法時。僅僅保證余數與被除數正負號同樣,商與被除數的符號無關。應盡量使n為無符號數。 - 7.9 toupper/tolower函數均採用int型參數,實現時要檢查輸入是否符合要求。採用置位實現非常高速。
- 7.11 要求一個按位輸出long型數字。須要考慮:不能對-n求值。可能溢出(邊界條件),應該把n轉換為負的再處理;余數的符號未知。應做歸一化處理。
- 7..2 atoi函數把字符串轉換為long型整數。應該依照負數來處理以避免溢出。
閱讀《C陷阱與缺陷》的知識增量