首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 專家編程筆記(第一章、第二章)核心知識點深度解析

     編輯:關於C語言

前言

本文整理自《C 專家編程》前兩章的核心內容,涵蓋 ANSI C 標準、const 關鍵字深度解析、整型提升與算術轉換、常見陷阱等專家級知識。適合進階 C 語言學習者查漏補缺。

1. 編譯器設計哲學:效率至上

編譯器設計者遵循「效率(幾乎)就是一切」的原則。這意味著標準留給實現的自由度很大,程序員不應依賴未定義行為或實現定義行為。

2. ANSI C 標準閱讀指南

建議直接閱讀 ANSI C 標準文檔。重點關注:

  • 參數傳遞限制:`char *` 可作為參數傳遞,但 `char **` 不行。傳遞多維數組時必須明確指定前 n-1 維的下標
  • 標準規定了「最小最大值」而非確切大小,如 `int` 至少 16 位

3. const 關鍵字深度解析

`const` 的位置決定其修飾對象,口訣:「右邊最近的類型才是被保護的」。

核心區別

聲明含義可修改指針值可修改指向內容
`const int *p` / `int const *p`指向常量的指針是否
`int * const p`常指針(指針本身是常量)否是
`const int * const p`指向常量的常指針否否

代碼演示

int main(void) {
    const int x = 2, y = 3;      // 兩個常量
    int z = 3;                   // 普通變量

    const int *pc = &x;          // 指向常量的指針
    int * const cp = &z;         // 常指針
    int *pp = &z;                // 普通指針

    pc = &z;     // ✓ pc 可指向其他變量
    // *pc = 10; // ✗ 不能通過 pc 修改值
    pc = pp;     // ✓ 普通指針賦值給指向常量的指針
    // pp = pc;  // ✗ 指向常量的指針不能賦值給普通指針(會丟失 const 屬性)

    *cp = x;     // ✓ 通過常指針修改指向變量的值
    // cp = &x;  // ✗ 常指針值不可修改

    // pp = &x;  // ✗ 不同編譯器行為不同,標準禁止
    // pp = pc;  // ✗ 同上
    pp = cp;     // ✓ 常指針可賦值給普通指針

    const int * const cpc = &x;  // 雙重 const
    return 0;
}

4. 整型提升與尋常算術轉換

整型提升規則

關鍵規則:

  • 小於 `int` 的整型(`char`、`short`、`bool`)參與運算前提升為 `int` 或 `unsigned int`
  • 運算時按「高精度」類型對齊:`long double` > `double` > `float` > `unsigned long long` > `long long` > `unsigned long` > `long` > `unsigned int` > `int`
  • sizeof 陷阱:`#define TOTAL_ELEMENTS (int)(sizeof(array)/sizeof(array[0]))` 必須強轉為 `int`,否則結果為 `size_t`(無符號),與有符號數比較會導致邏輯錯誤

5. 經典錯誤斷言

  • malloc 陷阱:`malloc(strlen(str))` 永遠是錯的,必須 `malloc(strlen(str) + 1)` 為 `\0` 預留空間
  • NUL vs NULL:`NUL`(單 L)是字符 `\0`,`NULL`(雙 L)是空指針宏
  • switch 缺失 break:97% 屬於錯誤;`default` 拼寫錯誤編譯器不報警
  • 字符串數組漏逗號:編譯器會悄無聲息合併相鄰字符串字面量

漏逗號示例

char *str[] = {
    "aaa", "bbb", "ccc", "ddd"
    "eee",  // 此處漏逗號,"ddd" 和 "eee" 合併為 "dddeee"
};
// 數組長度變為 4 而非 5

6. 靜態變量實現首次調用差異行為

函數內 `static` 變量僅初始化一次,可用於實現「首次調用與後續調用行為不同」的邏輯:

void func(void) {
    static int first_call = 1;
    if (first_call) {
        // 首次執行邏輯
        first_call = 0;
    } else {
        // 後續執行邏輯
    }
}

7. 逗號運算符優先級陷阱

`i = 1, 2;` 等價於 `(i = 1), 2`,結果 `i = 1`。
`i = (1, 2);` 結果 `i = 2`(逗號表達式取最後一個值)。

8. 運算順序與副作用

運算順序圖解

C 標準未規定函數參數求值順序(多數為右向左),且同一序列點多次修改同一對象屬於未定義行為:


i = 3; 
printf("%d, %d\n", i, i++);  // 輸出 4, 3(右向左求值)

i = 3; 
j = (++i) + (++i) + (++i);   // j = 16, i = 6(GCC,但標準未定義)

int i = 1, j = 1, k;
i = (i++) + (++i);  // i = 5(GCC)
k = (j++) + (++j);  // k = 4, j = 3

警告:上述多重副作用代碼屬於未定義行為,不同編譯器結果不同,實際工程中嚴禁編寫。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved