1. 程式人生 > >閱讀《C陷阱與缺陷》的知識增量

閱讀《C陷阱與缺陷》的知識增量

分配 之前 mon 釋放 title 無符號數 clean 改變 clear

版權聲明:本文為Focustc原創文章。轉載請註明作者及出處。 https://blog.csdn.net/caozhankui/article/details/35925939

看完《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
    的寫法是非法的(gcc4.8.2僅僅是警告)。
  • 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型整數。應該依照負數來處理以避免溢出。

  

轉載請註明作者:Focustc,博客地址為http://blog.csdn.net/caozhk,原文鏈接為點擊打開

閱讀《C陷阱與缺陷》的知識增量