在 C/C++ 中將多維數組傳遞給函數時,參數聲明與實際傳遞的地址類型必須精確匹配。本文通過一個計算學生成績的完整案例,系統梳理二維數組指針的定義、解引用與函數參數匹配規則。
search_score:顯示指定學生的 4 門課成績。count_avg:計算所有 12 個分數的平均值。#include <stdio.h>
void search_score(float (*p)[4], int n); // 形參:指向含 4 個 float 的數組的指針
void count_avg(float *p, int n); // 形參:指向 float 的指針(視為一維數組)
int main() {
// 3 行 4 列,行代表學生,列代表課程
static float scores[3][4] = {
{97, 45.5, 66, 77},
{88, 92.5, 78.5, 66},
{83, 74.5, 92, 100}
};
// 顯示第 0 位學生(索引 0)的成績
search_score(scores, 0);
// 計算所有分數平均值
// scores 退化為指向首行的指針,類型 float (*)[4]
// *scores 即 scores[0],類型 float[4],退化為 float*,指向首個元素
count_avg(*scores, 12); // 等價於 count_avg(scores[0], 12) 或 count_avg(&scores[0][0], 12)
return 0;
}
// 顯示第 n 位學生的 4 門課成績
void search_score(float (*p)[4], int n) {
// p 指向第 n 行(一維數組 float[4])
// p + n 指向第 n 行首地址
// *(p + n) 解引用得到第 n 行數組,退化為指向該行首元素的指針
// *(*(p + n) + i) 取第 i 門課分數
for (int i = 0; i < 4; ++i) {
printf("%7.2f", *(*(p + n) + i));
}
printf("\n");
}
// 計算 n 個 float 的平均值
void count_avg(float *p, int n) {
float *end = p + n; // 指向最後一個元素的下一個位置
float sum = 0.0f;
for (; p < end; ++p) {
sum += *p;
}
printf("avg=%7.2f\n", sum / n);
}
scores 類型:float (*)[4](指向含 4 個 float 的數組的指針)。scores[0] / *scores 類型:float[4],在表達式中退化為 float*,指向首元素 &scores[0][0]。&scores[0][0] 類型:float*,同上。| 實參表達式 | 類型 | 匹配形參 |
|---|---|---|
scores | float (*)[4] | float (*p)[4] |
scores[0] / *scores / &scores[0][0] | float* | float *p |
search_score 內部指針運算float (*p)[4]; // p 是行指針
p + n; // 偏移 n 行,類型仍為 float (*)[4]
*(p + n); // 取第 n 行數組,類型 float[4],退化為 float*
*(*(p + n) + i) // 取第 n 行第 i 列元素
count_avg 視為一維連續內存二維數組在內存中按行主序連續存儲,將首元素地址 &scores[0][0] 作為 float* 傳入,配合總元素個數 12,即可線性遍歷所有分數。
float *p[4]:這是指針數組(4 個 float*),而非指向二維數組的指針。正確寫法 float (*p)[4],括號不可省。scores 給 float**:二維數組不是指針的指針,內存佈局無指針層級,會導致非法內存訪問。float (*p)[4] 中的 4 必須與實際數組列數一致,否則指針運算偏移錯誤。C99 可用 VLA:float (*p)[cols]。多維數組傳參的本質是傳遞首元素地址,配合類型信息(列數)讓編譯器正確計算偏移。掌握 float (*p)[N] 與 float *p 兩種視角的轉換,是駕馭 C 語言指針系統的關鍵一環。