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

深入理解 C 語言陣列與指標:記憶體視角的差異與聯繫

     編輯:關於C

前言

承接上一篇《語義陷阱——陣列與指標》,本文從記憶體存取機制的更深層次,剖析陣列與指標的本質差異、何時可互換、以及為何函數參數會「退化」為指標。

何時陣列與指標相同?

根據 ANSI C 標準:

  1. **表達式中的陣列名** (非宣告場合) 被編譯器視為指向首元素的指標。
  2. **下標運算** a[i] 永遠等價於 *(a + i)。
  3. **函數參數宣告** 中的陣列名被視為指向首元素的指標。

因此,以下情況陣列與指標可互換:

  • 函數參數:void fun(int a[]) 與 void fun(int *a) 等價。
  • 表達式存取:a[i]、*(a+i)、*(p+i)、p[i] (假設 p=a) 效果相同。

記憶體存取機制差異

**陣列遍歷** (for(i=0;i<10;i++) a[i]=0;):

  1. 編譯期即知陣列首址 (左值),可載入暫存器 (R1) — 可提至迴圈外。
  2. 索引 i 位址固定 (R2),值載入 R3 — 可提至迴圈外。
  3. 計算有效位址:R4 = R1 + R3 * scale。
  4. 寫入 0 到 [R4]。

**指標遍歷** (p=a; for(i=0;i<10;i++) *p++=0;):

  1. 元素大小載入 R5 — 可提至迴圈外。
  2. 指標變數 p 本身位址載入 R1 — 可提至迴圈外。
  3. 讀取 p 的值 (目標位址) 到 R1。
  4. 寫入 0 到 [R1]。
  5. 更新 p:R1 += R5,寫回 p 的儲存位置。

**關鍵差異**:陣列名是 **編譯期常數位址** (左值不可修改);指標是 **執行期變數**,儲存著目標位址,需先讀取指標值才能間接存取。

為何 C 將陣列參數視為指標?

**效率**。C 採值傳遞,若陣列按值傳遞需複製整個陣列,耗時耗空。改為傳遞首址 (指標),函數直接操作原始記憶體,避免複製開銷。這也是 C++ 引入參考傳遞 (T&) 解決同類問題的前身邏輯。

陣列與指標可互換性總結

  1. a[i] 永遠被編譯器重寫為 *(a+i)。
  2. 指標永遠是指標,不會被重寫為陣列;但可對指標使用下標 (常用於函數參數)。
  3. **僅在函數參數宣告** 時,陣列宣告可寫作指標;函數呼叫時實際傳遞的是首址指標。
  4. 因此定義函數參數時,寫 int a[] 或 int *a 效果相同,函數內部拿到的都是指標。
  5. 其他所有場合 (外部宣告、定義、sizeof、&a 等),**宣告必須與定義匹配**:定義為陣列則外部宣告須為陣列;定義為指標則宣告為指標。

常見誤區釐清

  • extern char a[] 與 extern char a[100] 相容,因皆表示「某地址處有一陣列」。
  • extern char *a 與 char a[100] **不相容**:前者表示「某地址儲存著一個指標」,後者表示「該地址即是陣列」。混用會導致間接存取錯誤。
  • sizeof(a) 在陣列定義處給出整個陣列大小;在函數參數或表達式中 (已退化為指標) 則給出指標大小。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved