C語言易錯點總結

  1. 所有的註釋都會被預處理器用空格進行替換,因此註釋可以出現在任何空格可以出現的地方。
  2. 除了數組名被用作運算符sizeof的參數這一情況,其他所有情形代表指向數組中下標爲0的元素的指針。
  3. C語言使用=作爲賦值運算符,==作爲比較運算符的原因是賦值在代碼中更常見,這樣可以減少代碼長度
  4. C編譯器將程序分解成符號的方法是貪心法
  5. 除了字符串和字符常量,符號的中間不能嵌有空白(空格符,製表符和換行符)
  6. 用單引號括起一個字符代表一個整數,用雙引號括起一個字符代表一個指向無名數組起始字符的指針
  7. ‘abc’在vs中保存的最後一個字符,前面的被覆蓋
  8. (a++)++表達式報錯,自增和自減操作符的操作數必須是一個“左值”,而a++的結果是“右值”。
  9. 只有4種操作符的結果是“左值”,分別是[], ., ->, *
  10. 運算符的優先級:[],(),->,.最高,其次是單目運算符,接着是算術>移位>關係>邏輯>賦值>條件(?:),最後是逗號,只有單目、三目和賦值運算符是自右向左結合的
  11. 注意懸掛else
  12. 如果f是一個函數名,則f;語句計算函數f的地址,卻不調用
  13. 在java和Python中,一個語句要麼按照我們的預期正確執行,要麼立即拋出異常,但在C++中還有一種情況是邏輯上出錯了,C++標準卻沒有規定怎麼處理。
  14. C語言中只有四個運算符(&&、||、?:和,)存在規定的求值順序,其他所有運算符對其操作數求值的順序是未定義的。逗號運算符先對左側操作數求值,“丟棄”後再對右側操作數求值。
  15. 分隔函數參數的逗號並非逗號運算符,所以在f(x,y)中的x,y的求值順序是未定義的,而在g((x,y))中則是先對x求值,在對y求值,函數g只有一個參數
  16. 按位運算符(&、|、~)和邏輯運算符(&&、||、!)某些時候可以互換,不過只是因爲巧合
  17. 函數main的返回值告知操作系統該函數的執行是成功還是失敗,典型的處理方案是返回值爲0表示執行成功,非0爲失敗
  18. 當兩個操作數都是有符號整數時,“溢出”就可能發生,“溢出”的結果是未定義的。正確做法是強制轉換爲無符號整數或者將加法轉換爲減法
  19. 如果p是一個空指針,則printf(“%s”,p);的行爲是未定義的。
  20. 數組下標從0開始和不對稱邊界是對程序設計的簡化
  21. 一個返回值爲整型的函數如果返回失敗,實際上是隱含地返回了某個“垃圾”整數,只要該值不被用到,就無關緊要
  22. 如果一個未聲明的標識符後跟一個開括號,那麼它將被視爲一個返回整型的函數
  23. 每個外部對象都必須在程序某個地方進行定義。如果一個程序中包括了語句extern int a;那麼這個程序必須在別的某個地方包括int a;這個兩個語句既可以位於同一個源文件中,也可以位於不同源文件中
  24. 如果一個程序對同一個外部變量的定義不止一次,結果是未定義的。例如:int a=1;出現在一個文件中,而int a=2;出現在另一個文件中,結果與系統有關。如果一個外部變量在多個源文件中定義卻並沒有指定初始值,那麼某些系統會接受,另一些系統則不會接受。唯一的解決辦法就是每個外部變量只定義一次。
  25. 如果在一個文件中定義char filename[] = “hello”,而在另一個文件中聲明extern char* filename;在打印filename時,程序將不能正常工作
  26. 爲了避免可能出現的命名衝突,如果一個函數僅僅被同一個源文件中的其他函數調用,則應該用static聲明該函數
  27. getchar()返回類型爲int(因爲EOF是int類型),如果定義一個char類型變量接收,則程序可能不能正常工作
  28. 爲了保持與過去不能同時進行文件讀寫操作的程序的向下兼容性,一個輸入操作不能隨後緊跟一個輸出操作,反之亦然。如果要同時操作的話,必須在中間插入fseek函數的調用,其作用是改變文件狀態
  29. C語言中僅有4種基本數據類型——整型、浮點型、指針和聚合類型(數組和結構等)
  30. 整型家族包括字符、短整型、整型和長整型,分爲有符號和無符號兩種。長整型至少應該和整型一樣長,而整型至少應該和短整型一樣長。
  31. Literal有時譯爲字面值,有時譯爲常量,它們含義相同,只是表達習慣不一。其中,string literal和char literal分別譯爲字符串常量和字符常量,其他的literal一般譯爲字面值
  32. 對於使用不同字符集的系統,使用字符常量可以提高程序的可移植性
  33. 枚舉類型就是指它的值爲符號常量而不是字面值的類型,其變量用整型存儲
  34. 所有字符和NUL終止符都存儲於內存的某個位置,具有相同的值的不同字符串在內存中是分開存儲的。ANSIC聲明如果對一個字符串常量進行修改,結果是未定義的
  35. 鏈接屬性一共有3種——external(外部),internal(內部)和none(無),沒有鏈接屬性的標識符(none)總是被當做單獨的個體,也就是說該標識符的多個聲明被當做獨立不同的實體
  36. 如果extern用於同一標識符的第2次或以後的聲明時,不會更改由第一次聲明所指定的鏈接屬性
  37. 函數的形式參數不能聲明爲靜態,因爲實參總是在堆棧中傳遞給函數,用於支持遞歸
  38. 由於寄存器值的保存和恢復,某個特定的寄存器在不同的時刻所保存的值不一定相同,所以機器不提供寄存器變量的地址
  39. 由於顯示的初始化將在代碼塊的起始處插入一條隱式的賦值語句,因此在聲明變量的同時進行初始化和先聲明後賦值只有風格只差,並無效率之別
  40. C語言不存在專門的“賦值語句”,賦值是一種操作,在表達式內進行
  41. 一個程序如果使用了有符號數的右移位操作,則不可移植
  42. 自增和自減操作符要求操作數必須是一個“左值”,因爲操作符的結果是變量值的拷貝
  43. 逗號表達式自左至右逐個進行求值,整個表達式的值就是最後那個表達式的值
  44. 間接訪問和下標引用的結果是個左值,其餘操作符的結果則是右值
  45. 整型算術運算總是至少以缺省整型類型的精度來進行的,所以字符型和短整型操作數在使用之前被轉換爲普通整型,這種轉換稱爲整型提升
  46. 把函數調用以操作符的方式實現意味着“表達式”可以代替“常量”作爲函數名
  47. 變量名與內存位置之間的關聯並不是硬件提供的,而是編譯器實現的,變量名使得我們更方便記住地址,然而硬件仍然通過地址訪問內存位置
  48. 不能簡單地通過檢查一個值的位來判斷它的類型,必須觀察程序中這個值的使用方式,值的類型並非值本身所固有的一種特性,而是取決於它的使用方式
  49. 機器內部NULL指針的實際值不一定是零值,編譯器負責零值和內部值之間的翻譯轉換
  50. 對一個NULL指針進行間接訪問,結果是未定義的
  51. 在表達式兩端加上括號總是合法的
  52. 當程序調用一個無法見到原型的函數時,編譯器認爲該函數返回一個整型值
  53. 一個沒有參數的函數的原型應該聲明爲“int *func(void);”而不是“int *func();”,後者表示只給出func函數的返回類型,目的是爲了保持與ANSI標準之前的程序的兼容性
  54. 閱讀遞歸函數最容易的方法不是糾纏於它的執行過程,而是相信遞歸函數會順利完成它的任務。如果你的每個步驟正確無誤,你的限制條件設置正確,並且每次調用之後更接近限制條件,遞歸函數總是能夠正確地完成任務。
  55. Strcmp函數的返回值不應當作布爾值測試,也不應與1和-1進行比較
  56. 具有相同成員列表的結構聲明產生不同類型的變量
  57. union變量可以被初始化,但初始值必須是union第一個成員的類型,而且它必須位於一對花括號裏面
  58. 在許多機器中,可以把函數參數聲明爲寄存器變量,進一步提高指針傳遞的效率
  59. 常見的動態內存錯誤:對NULL指針進行解引用操作、對分配的內存進行操作時越過邊界、釋放非動態分配的內存、試圖釋放一塊動態分配的內存的一部分以及一塊動態內存被釋放之後繼續使用
  60. 不要僅僅根據代碼的大小評估它的質量
  61. #progma是不可移植的,預處理器忽略它不認識的#progma指令,兩個不同的編譯器可能以兩種不同的方式解釋同一條#progma指令
  62. 不要在一個宏定義的末尾加上分號,使其成爲一條完整的語句
  63. 標準輸入是缺省的輸入設置,標準輸出是缺省的輸出設置,具體的缺省值因編譯器而異。標準錯誤就是錯誤信息寫入的地方,爲錯誤信息準備一個不同的流意味着,即使標準輸出重定向到其他地方,錯誤信息仍將出現在屏幕或其他缺省的輸出設備上
  64. 對於輸出流,fclose函數在文件關閉之前刷新緩衝區
  65. 環境就是一個由編譯器定義的名字/值對的列表,由操作系統維護
  66. 當exit函數被調用時,所有被atexit函數註冊爲退出函數的函數將按照它們註冊的順序被反序依次調用
  67. System函數把它的字符串參數傳遞給宿主操作系統,作爲一條命令,由系統的命令處理器執行
  68. 使用斷言檢查內存是否分配成功是危險的
  69. 使用斷言來防止非法操作,可以簡化程序的調試
  70. Assert是一個宏,只適用於驗證必須爲真的表達式
  71. 迭代比尾部遞歸效率更高
  72. 局部變量聲明和函數原型並不會產生任何彙編代碼,但如果局部變量在聲明時進行了初始化,就會出現指令用於執行賦值操作
  73. 是鏈接器而不是編譯器決定外部標識符的最大長度
  74. 無法鏈接由不同編譯器產生的程序
  75. 對信號進行處理將導致程序的可移植性變差
  76. 函數longjmp不能返回到一個已經不再處於活動狀態的函數
  77. 函數clock可能只產生處理器時間的近似值
  78. 從異步信號的處理函數中調用exit或abort函數是不安全的
  79. 當每次信號發生時,必須重新設置信號處理函數
  80. 避免exit函數的多重調用
  81. 打印長整數時,使用l修飾符可以提高可移植性
  82. 不要忘了在一條調試用的printf語句後面跟一個fflush調用
  83. 改變文件的位置將丟棄任何被退回到流的字符
  84. 如果流當前處於文件尾,feof函數返回真。這個狀態可以通過對流執行fseek、rewind或fsetpos函數來清除
  85. 在宿主式運行時環境中,操作系統可能執行自己的緩衝方式,不依賴於流
  86. 以#符號開頭,後面不跟任何內容的一行,這條指令是無效指令,被預處理器簡單地刪除
  87. “xyz”+1表達式的結果是指針,指向字符串中的第2個字符:y
  88. *”xyz”表達式的結果是x,”xyz”[2]的結果是z
  89. cdecl程序可以幫助你分析複雜的聲明

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章