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

C 語言筆試經典題:指針運算與函數參數求值順序陷阱

     編輯:關於C

題目代碼

int main(void) {
    int a[] = {6, 7, 8, 9, 10};
    int *p = a;
    *(p++) += 123;   // 關鍵行
    printf("%d, %d\n", *p, *(++p));
    return 0;
}

輸出結果

8, 8

逐步解析

1. *(p++) += 123; 實際做了什麼?

  • p++ 後置遞增:表達式值為舊 p(指向 a[0]),副作用是 p 指向 a[1]。
  • *p 解引用得到 a[0](值 6)。
  • += 123 將 a[0] 修改為 129。
  • **結論**:該語句對後續邏輯的唯一可見副作用是 p 向後移動一個位置;a[0] 被修改但後續未讀取。

2. printf("%d, %d\n", *p, *(++p)); 參數求值順序

  • C 標準**未規定**函數參數求值順序( unspecified behavior )。
  • 絕大多數編譯器(x86 cdecl 呼叫慣例)採用**從右向左**壓棧:先求第二個參數,再求第一個參數。
  • 本題環境下:
    1. 先算 *(++p):p 從 a[1] 移至 a[2],值為 8,壓棧。
    2. 再算 *p:p 現指向 a[2],值同為 8,壓棧。
    3. printf 彈出並輸出:8, 8。

深度延伸:+= 與 = 的陷阱

*(p++) += 123;          // 原寫法:p 遞增一次,a[0] += 123
*(p++) = *(p++) + 123;  // 替換後:p 遞增兩次!a[0] = a[1] + 123
  • 原寫法:p++ 只求值一次,副作用一次。
  • 替換寫法:兩個 p++ 各自求值、各自副作用 → p 移動兩格,讀取 a[1] (7) + 123 = 130 寫入 a[0],p 最終指向 a[2]。
  • 若換成替換寫法,輸出變為 9, 9(a[2] 原值 8,經 ++p 指向 a[3] 值 9)。

核心知識點總結

知識點說明
後置遞增 p++值為舊指針,副作用:指針向後移動一個元素
前置遞增 ++p副作用先行:指針移動,值為新指針所指內容
複合賦值 +=左運算元只求值一次(C 標準保證)
函數參數求值順序C 標準未定義,視編譯器/平台而定(常見右→左)
序列點語句結尾 ;、函數調用前、&& || ?: , 運算符;副作用在序列點前完成

最佳實踐建議

  1. **避免**在同一表達式中對同一變數多次修改或「修改後又讀取」(如 printf("%d %d", *p, *(++p)))。
  2. 將指針運算與解引用拆分為多條語句,明確序列點。
  3. 筆試遇到此類題:**先畫圖追蹤指針位置**,再根據目標平台慣例推導參數求值順序,最後註明「行為未定義/視實現而定」。
  4. 現代編譯器(GCC/Clang/MSVC)在高警告等級下會提示:warning: operation on 'p' may be undefined。

「寫出讓編譯器、讀者、未來的自己都能輕鬆理解的代碼,才是真正的基本功。」

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