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

C語言二維數組作為函數參數的五種傳遞方式詳解

     編輯:關於C語言

核心難點:參數退化與類型匹配

C 語言中,二維數組作為函數參數時會退化為指向一維數組的指針(即 int (*)[N]),而非 int **。編譯器需要知道列數(第二維大小)才能正確計算內存偏移(a[i][j] == *(*(a + i) + j))。因此,函數簽名必須包含列數,行數可省略。

五種標準傳遞方式

方式一:指定列數的二維數組語法(最常用)

// 行數可省略,列數必須是常量表達式
void func1(int arr[][3], int rows);
void func1(int arr[5][3], int rows); // 等價,行數 5 被忽略

// 調用
int a[5][3] = {{1,2,3},{4,5,6},{7,8,9},{10,11,12},{13,14,15}};
func1(a, 5);

優點:語法直觀,調用方直接傳數組名,內部可直接用 arr[i][j] 訪問。
限制:列數必須是編譯期常量,不支持變長數組(VLA)作為形參(C99 允許 VLA 形參,但需傳遞維度參數)。

方式二:指向一維數組的指針(本質等價)

void func2(int (*arr)[3], int rows); // arr 是指向「含 3 個 int 的數組」的指針

// 內部訪問同樣用 arr[i][j]
// 調用方式相同:func2(a, 5);

這是方式一的等價寫法,更能體現「退化為指針」的本質。

方式三:二維數組引用(C++ 專有,C 不支持)

// C++ 專屬,需指定行列數
void func3(int (&arr)[5][3]); // 行列數都固定,靈活性差

注意:純 C 編譯器(GCC -std=c11, MSVC C 模式)不支持此語法。

方式四:指向二維數組的指針(雙重指針語法,實為一級指針)

void func4(int (*arr)[5][3]); // 指向「5行3列整型數組」的指針

// 調用需取地址
func4(&a);
// 內部訪問:(*arr)[i][j]

用於需要在函數內修改指針本身指向的場景,較少見。

方式五:動態分配的二維數組 → 雙重指針 int **(完全不同的內存佈局)

// 適用於運行時動態分配行指針數組 + 每行獨立 malloc
void func5(int **arr, int rows, int cols) {
    // 訪問:arr[i][j] 正常工作
    // 但無法直接傳入靜態二維數組 int a[5][3]!
}

// 動態分配示例
int **p = malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++) p[i] = malloc(cols * sizeof(int));
// 釋放時需逐行 free 再 free(p)

關鍵區別:

特性靜態二維數組 int a[5][3]動態雙重指針 int **p
內存佈局連續塊(5×3 個 int)行指針數組 + 離散行數據
類型int (*)[3]int **
函數參數int arr[][3] 或 int (*arr)[3]int **arr
訪問語法arr[i][j]arr[i][j](編譯器生成雙重間接)
跨函數傳遞直接傳名傳 int **

常見錯誤與避坑指南

  1. ❌ void f(int **a) 接收靜態二維數組 → 類型不匹配,運行時崩潰(訪問邏輯錯誤)。
  2. ❌ 列數用變量:void f(int a[][n])(C89 非法,C99 VLA 形參合法但需傳 n)。
  3. ✅ 正確做法:列數用宏/常量 #define COLS 3,形參寫 int a[][COLS]。
  4. ✅ 動態二維數組:若行列均在運行時確定,用方式五(int **)或單塊連續內存 int *p = malloc(rows*cols*sizeof(int)) 並手動計算索引 p[i*cols+j]。

完整示例:多種方式混用

#include <stdio.h>
#include <stdlib.h>

#define COLS 3

void print_arr1(int arr[][COLS], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < COLS; j++) printf("%d ", arr[i][j]);
        printf("\n");
    }
}

void print_arr2(int (*arr)[COLS], int rows) { // 等價寫法
    print_arr1(arr, rows);
}

int main() {
    int a[2][COLS] = {{1,2,3},{4,5,6}};
    printf("方式一/二:\n");
    print_arr1(a, 2);

    // 動態分配演示方式五
    int rows = 2, cols = COLS;
    int **p = malloc(rows * sizeof(int *));
    for (int i = 0; i < rows; i++) {
        p[i] = malloc(cols * sizeof(int));
        for (int j = 0; j < cols; j++) p[i][j] = a[i][j];
    }
    printf("方式五 (int **):\n");
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) printf("%d ", p[i][j]);
        printf("\n");
    }
    for (int i = 0; i < rows; i++) free(p[i]);
    free(p);
    return 0;
}

總結

二維數組傳參的核心是「列數固定、退化為指向一維數組的指針」。掌握 int arr[][COLS] / int (*arr)[COLS] 兩種主流寫法,明確區分靜態二維數組與動態雙重指針的內存佈局與類型差異,即可避開 90% 的坑。C99 VLA 形參(void f(int rows, int cols, int arr[rows][cols]))是現代 C 的推薦寫法,但需注意編譯器支持度(MSVC 不支持)。

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