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

C語言二維數組指針完全圖解:行指針、列指針與內存佈局

     編輯:關於C語言

前言:二維數組指針為何讓人頭大?

int arr[3][4] 在內存中是連續 12 個 int,但涉及 arr、arr[0]、*arr、arr+1、*(arr+1) 等多種表達式時,類型與含義截然不同。本文通過實測地址輸出、圖解內存佈局、類型推導三步法,徹底搞懂二維數組指針。

一、實驗代碼與輸出


#include <stdio.h>
int main() {
    int arr[3][4] = {{11,12,13,14},{21,22,23,24},{31,32,33,34}};
    int i, j;

    // 打印數組內容
    for (i=0;i<3;i++) {
        for (j=0;j<4;j++) printf("%d ", arr[i][j]);
        printf("\n");
    }

    // 關鍵地址打印(假設 32 位系統,地址為負數屬正常補碼顯示)
    printf("arr           = %p\n", (void*)arr);           // 數組首地址
    printf("&arr[0][1]    = %p\n", (void*)&arr[0][1]);    // 第0行第1列
    printf("arr[0]        = %p\n", (void*)arr[0]);        // 第0行首地址
    printf("arr+1         = %p\n", (void*)(arr+1));       // 第1行首地址(跨過 4 個 int)
    printf("*(arr+1)      = %p\n", (void*)*(arr+1));      // 同 arr[1],第1行首地址
    printf("*(*(arr+1))   = %d\n", *(*(arr+1)));          // arr[1][0] = 21
    printf("*(*(arr+1)+1) = %d\n", *(*(arr+1)+1));        // arr[1][1] = 22
    return 0;
}

輸出(地址示意)


11 12 13 14
21 22 23 24
31 32 33 34
arr           = 0x7ffee4a0
&arr[0][1]    = 0x7ffee4a4
arr[0]        = 0x7ffee4a0
arr+1         = 0x7ffee4b0   // +16 bytes = 4 * sizeof(int)
*(arr+1)      = 0x7ffee4b0
*(*(arr+1))   = 21
*(*(arr+1)+1) = 22

二、內存佈局圖解

地址遞增 →
+------+------+------+------+------+------+------+------+------+------+------+------+
| 11   | 12   | 13   | 14   | 21   | 22   | 23   | 24   | 31   | 32   | 33   | 34   |
+------+------+------+------+------+------+------+------+------+------+------+------+
  arr[0][0]                    arr[1][0]                    arr[2][0]
  arr (int(*)[4])              arr+1 (int(*)[4])            arr+2
  arr[0] (int*)                arr[1] (int*)                arr[2] (int*)

三、核心類型推導表

表達式類型含義地址值
arrint (*)[4]指向含 4 個 int 陣列的指針(行指針)首地址
arr[0] / *arrint*第 0 行首元素地址(列指針)首地址
arr + iint (*)[4]指向第 i 行的行指針基址 + i * 4 * sizeof(int)
*(arr + i) / arr[i]int*第 i 行首元素地址基址 + i * 4 * sizeof(int)
*(arr + i) + j / arr[i] + jint*第 i 行第 j 列元素地址基址 + (i*4+j)*sizeof(int)
*(*(arr + i) + j) / arr[i][j]int第 i 行第 j 列元素值—

四、關鍵結論:三個等價關係

  1. 行指針:arr + i == &arr[i](類型 int(*)[4])
  2. 列指針:*(arr + i) + j == arr[i] + j == &arr[i][j](類型 int*)
  3. 取值:*(*(arr + i) + j) == *(arr[i] + j) == arr[i][j]

五、函數傳參陷阱與正確寫法

❌ 錯誤:


void foo(int **p) { ... } // int** 不能接收 int[3][4]
foo(arr); // 編譯錯誤/警告

✅ 正確三種寫法:


// 1. 明確列數(最常用)
void foo(int arr[][4], int rows) { ... }
void foo(int (*arr)[4], int rows) { ... } // 等價

// 2. 一維扁平化(通用,無列數限制)
void foo(int *arr, int rows, int cols) {
    arr[i*cols + j] = ...;
}

// 3. C99 VLA(列數動態)
void foo(int rows, int cols, int arr[rows][cols]) { ... }

六、總結口訣

數組名 arr 是「行指針」,arr+1 跨過一整行;
解引用一次 *(arr+i) 變「列指針」,指向行首;
再加列偏移 +j 取元素地址,再解引用取值。
函數傳參必須保留列數,或扁平化為一維指針。

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