數組與指針是 C 語言最易混淆又最核心的關係。本節通過 sizeof 實測、函數參數退化、內存佈局圖解,徹底釐清二者異同,建立正確直覺。
內存佈局:"Shatner"
索引: 0 1 2 3 4 5 6 7
內容: S h a t n e r \0
地址: 100 101 102 103 104 105 106 107
```
C 不記錄數組長度,約定 \0 (ASCII 0) 為結束標記。sizeof 可獲取佔用字節數(含結束符)。
二、sizeof 陷阱實測
代碼:sizeof_demo.c
#include
void SayHello(char msg[]) {
printf("msg occupies: %zu\n", sizeof(msg)); // 輸出指針大小!
}
int main() {
printf("Shatner occupies: %zu\n", sizeof("Shatner")); // 8 (含\0)
char msg[] = "Hello C";
printf("msg occupies: %zu\n", sizeof(msg)); // 8 (含\0)
SayHello(msg); // 輸出 8 (64位) 或 4 (32位)
char *msgp = msg;
printf("msgp occupies: %zu\n", sizeof(msgp)); // 8/4 指針大小
return 0;
}
輸出結果(64位系統)
Shatner occupies: 8
msg occupies: 8
msg occupies: 8 // 函數內:指針大小!
msgp occupies: 8
核心解析
表達式 類型 sizeof 結果 原因
"Shatner"char[8]8 字符串字面量是數組
msg (在 main)char[8]8 數組聲明分配內存
msg (在 SayHello)char*8/4 參數退化為指針
msgpchar*8/4 指針變量
三、數組參數退化鐵律
void func(int arr[10]) // 等價於
void func(int arr[]) // 等價於
void func(int *arr) // 完全一致!
編譯器處理:函數參數聲明為數組時,自動轉換為指針。數組大小信息丟失,sizeof 僅得指針大小。
四、數組 vs 指針:本質區別
特性 數組 指針
內存分配 編譯期/棧上分配元素空間 僅分配指針變量空間(8/4字節)
地址值 &arr == &arr[0] (值相同)&p != p (指針變量自身地址 vs 指向地址)
可修改性 不可修改地址 (非左值) 可修改指向 p = &other;
sizeof 整個數組字節數 指針變量大小
類型 int[5]int*
五、&arr 與 &arr[0] 的類型差異
int arr[5];
int *p1 = &arr[0]; // int* - 指向首元素
int (*p2)[5] = &arr; // int(*)[5] - 指向整個數組
printf("%p %p\n", p1, p2); // 值相同
printf("%p %p\n", p1+1, p2+1); // 差異巨大!
// p1+1: +4 字節 (下一個 int)
// p2+1: +20 字節 (下一個 int[5])
六、實戰:在函數中獲取數組長度
錯誤嘗試:
void bad_len(int arr[]) {
int n = sizeof(arr) / sizeof(arr[0]); // 永遠是 1 (指針/元素)
}
正確方案:
- 額外傳遞長度:
void good_len(int arr[], int n)
- 結構體封裝:
typedef struct { int *data; int len; } IntArray;
- 哨兵值:字符串用
\0,整數數組可用特定值(如 -1)結尾
- 宏計算:
#define ARR_LEN(arr) (sizeof(arr)/sizeof(arr[0])) **僅在數組聲明作用域有效**
七、指針算術與數組索引等價性
int arr[5] = {10,20,30,40,50};
int *p = arr;
arr[2] // 30
*(arr+2) // 30
p[2] // 30
*(p+2) // 30
// 完全等價!編譯器將 a[b] 統一翻譯為 *(a+b)
八、二維數組與指針
int matrix[3][4];
// 類型:int[3][4] - 3行4列
// 內存連續佈局:row0_col0, row0_col1... row2_col3
int (*p)[4] = matrix; // 指向「4個int的數組」的指針
// p[0] → 第0行 (int[4])
// p[1] → 第1行
// p[i][j] 等價於 *(*(p+i)+j) 等價於 matrix[i][j]
九、本節作業
- 編寫函數
void print_arr(int *arr, int n) 打印數組
- 實驗:
int a[5]; printf("%p %p\n", a, &a); printf("%p %p\n", a+1, &a+1); 觀察地址增量
- 閱讀
man 3 strlen 理解字符串長度計算(尋找 \0)
- 思考:為什麼 C 設計數組參數退化為指針?(提示:性能、兼容性)
總結
「數組在聲明處是數組,在表達式中(含參數傳遞)退化為指針」。牢記:sizeof 區分二者,&arr 獲取數組指針,函數傳遞需額外帶長度。這是 C 語言「手動管理」哲學的核心體現——**沒有免費的午餐,長度信息不自動傳遞**。
C++ const 關鍵字完全指南:從基礎用法到進階應用
C# WinForm 讀取 App.config 數據庫連接字符串實戰
C標準庫函數速查手冊(C 開頭):cabs/calloc/ceil/cgets/chdir/chmod/chsize/circle/cleardevice/clearerr/clearviewport/close/clock/closegraph/clreol/clrscr/coreleft/cos/cosh/country/cprintf/cputs/creat/creatnew/creattemp/cscanf/ctime/ctrlbrk
BigArithmetic:基於快速傅立葉變換的任意精度算術運算靜態類實現
C# 圖形開發之旅:打造動態橢圓形按鈕控件(第五講·完結篇)
.NET Framework I/O 架構:同步與非同步檔案操作總覽
C# 面向對象三大支柱:封裝、繼承、多態深度解析與實戰範例
Copyright © 程式碼之家 All Rights Reserved