跳轉到內容

C 程式設計/C 的特點

來自華夏公益教科書,開放的書籍,開放的世界

C 是一種高效、簡約的語言,它有一些程式設計師必須注意的特殊之處。為了解決這些問題,有時一個好的解決方案是將另一種語言與 C 結合使用,以獲得額外的靈活性和功能,例如 Emacs-LISP 和 C 的組合用於 Emacs。有時可以透過使用保證功能和安全的特殊結構來解決它們,但代價是速度變慢和複雜度增加。然而,大多數情況下,透過實踐,C 程式設計師不會遇到這裡提到的問題,並且更喜歡使用一種與通用馮·諾依曼硬體架構緊密匹配的語言。

以下是 ANSI C 中的一些特點(有時也是它的優勢),一些小,一些大

陣列和指標之間缺乏區別
最早的 C(大約 1973 年)根本沒有陣列;現代實現是記憶體中連續的區域,使用指標算術進行訪問(注意:宣告的陣列不能像指標一樣賦值),這避免了使用固定大小宣告陣列的必要性。但是,這種能力在使用不當的情況下會導致緩衝區溢位錯誤。
陣列不儲存它們的長度
上述特性的一個後果。這意味著程式可能需要在訪問陣列之前顯式地執行邊界檢查。除非函式被傳遞一個固定大小的陣列,否則它無法發現它被傳遞的陣列的長度:因此,函式必須被傳遞長度,也許作為單獨的變數傳遞給函式或在結構中傳遞。因此,大多數實現不提供自動陣列邊界檢查,而手動邊界檢查容易出錯。
如果 C(或 C++)程式試圖訪問超出實際分配記憶體的陣列元素,則會發生緩衝區溢位,通常會使程式崩潰。緩衝區溢位錯誤也是常見的安全漏洞。許多其他計算機語言提供自動邊界檢查,因此它們幾乎不受此類錯誤的影響。[1][2][3][4][5]
可變長度陣列
VLA ‒ 可變長度陣列 ‒ 只能用於函式引數和自動變數。VLA 不能在結構體內部使用(除非作為結構體的最後一個專案)。不可能定義一個對應於標準 Forth 字典定義(它有兩個可變長度部分)的結構,除非作為 char 的未區分陣列。
內建的 2D 或 3D 陣列的大小不受限制
此功能已從 C99 規範開始新增,用於可變長度陣列,儘管許多 C 編譯器仍然不支援它。如果沒有 VLA,函式無法接受任意大小的 2D 或 3D 陣列。特別是,不可能定義一個接受 int a[5][4][3]; 在一次呼叫中,並在以後呼叫中接受 int b[10][10][10]; 。而不是使用內建的 2D 或 3D 陣列資料型別,C 程式設計師使用其他資料型別來儲存(數學)任意大小的 2D 或 3D 陣列(多維陣列) - 請參見 C 程式設計/常見做法#動態多維陣列 瞭解詳細資訊。
沒有正式的字串資料型別
字串是字元陣列(缺乏任何抽象),並繼承了它們的所有約束(結構可以在一定程度上提供抽象)。
型別安全性較弱
C 的型別安全性不是很高。記憶體管理函式在無型別指標上操作,沒有內建的執行時型別強制,並且可以透過指標和強制轉換來繞過型別系統。此外,typedef 不會建立新型別,而只是建立別名,因此它僅用於程式碼可讀性。但是,可以使用單成員結構來強制型別安全性。
沒有垃圾回收
作為一種旨在最小化開銷的低階語言,C 僅提供手動記憶體管理,這可能導致簡單的記憶體洩漏不受控制地繼續。
區域性變數在宣告時未初始化
區域性變數(但不是全域性變數)必須手動初始化;在此之前,它們包含宣告時記憶體中的任何內容。這並不罕見,但 C 標準並不禁止訪問未初始化的變數(這是)。
笨拙的函式指標語法
函式指標採用 [返回值型別] [名稱]([引數 1 型別])([引數 2 型別]) 的形式,使得它們難以使用。typedef 可以緩解這種繁重的語法。例如,typedef int fn(int i);。有關更多詳細資訊,請參見 C 程式設計/指標和陣列#指向函式的指標
沒有反射
C 程式在執行時無法將字串評估為源 C 程式碼語句。
巢狀函式不是標準
但是,許多 C 編譯器確實支援巢狀函式,包括 GNU C。[6]
沒有正式的異常處理
一些標準函式返回特殊值,必須手動處理。例如,malloc() 在失敗時返回 null。例如,必須將 getchar() 的返回值儲存在 int 中(而不是預期的那樣,儲存在 char 中),以便可靠地檢測檔案結束 - 請參見 EOF 陷阱。不包含適當錯誤處理的程式在大多數情況下可能執行良好,但在出現異常情況時可能會崩潰或出現其他故障。POSIX 系統通常使用 signal() 來處理某些型別的異常。(有關詳細資訊,請參見 C 程式設計/錯誤處理#訊號)。一些程式使用 setjmp()longjmp()goto 來手動處理某些型別的異常。(有關詳細資訊,請參見 C 程式設計/控制#最後一點:gotoC 程式設計/協程)。
沒有匿名函式定義

參考文獻

[編輯 | 編輯原始碼]
華夏公益教科書