在C語言編程中,數組看似簡單,實則暗藏玄機。早期寫代碼時,我習慣直接定義數組隨意使用,直到遇到棘手問題才發現:數組與指針的關係遠比想像中複雜。本文將通過實例深入剖析數組名、指針運算及sizeof運算符在不同語境下的行為差異。
假設定義一個整型數組:int cc[10];
圍繞這個數組,衍生出多種指針表達式:cc、cc+1、&cc[0]、&cc、&cc+1。它們的含義各不相同,請先思考,再參考下面的測試代碼:
#include <stdio.h>
int main() {
int cc[10];
printf("%p\n", (void*)cc);
printf("%p\n", (void*)(cc+1));
printf("%p\n", (void*)&cc[0]);
printf("%p\n", (void*)&cc);
printf("%p\n", (void*)(&cc+1));
getchar();
return 0;
}
cc:數組名,在大多數表達式中自動轉換為指向首元素的指針(int*),值為首元素地址。cc+1:指向第二個元素的指針,地址增加 sizeof(int) 字節(32位機上為4字節)。&cc[0]:顯式取首元素地址,類型為 int*,值等同於 cc。&cc:指向整個數組的指針,類型為 int (*)[10]。雖然數值上等於首地址,但語義完全不同——它代表數組整體。&cc+1:指向數組末尾後一個位置的指針,地址增加 sizeof(cc)(即 40 字節),而非 4 字節。
圖中清晰展示:&cc 的類型是 int(*)[10],因此 &cc+1 跳過整個數組大小。這驗證了數組名不是普通指針,它是數組對象的標識符,是一個不可修改的左值。
根據 C 標準,數組名只有在以下兩種情況下保持「數組類型左值」身份:
sizeof 運算符的操作數& 取地址運算符的操作數其餘所有場合,數組名自動退化為「指向首元素的指針常量」(右值)。這就是為什麼 sizeof(cc) 返回 40,而 sizeof(&cc) 卻返回 4(指針大小)。
#include <stdio.h>
int main() {
int cc[10];
printf("%zu\n", sizeof(cc[0])); // 4:單個 int 大小
printf("%zu\n", sizeof(cc)); // 40:整個數組大小(10*4)
printf("%zu\n", sizeof(&cc)); // 4:指向數組的指針大小
printf("%zu\n", sizeof(int(*)[10])); // 4:同上
getchar();
return 0;
}
#include <stdio.h>
int main() {
int *p[2]; // 指針數組:兩個 int* 元素
printf("%p\n", (void*)*p); // 未初始化,輸出隨機值
printf("%zu\n", sizeof(p)); // 8:兩個指針大小 (2*4)
printf("%zu\n", sizeof(&p)); // 4:指向指針數組的指針大小
getchar();
return 0;
}
#include <stdio.h>
int main() {
char str[] = "hactrox"; // 棧上數組,包含結束符 '\0'
char *p = str; // 指向首字符
printf("%zu %zu\n", sizeof(str), sizeof(p)); // 8, 4
getchar();
return 0;
}
str 是字符數組,sizeof(str) 計算包含 '\0' 在內的 8 字節;p 是指針,大小固定為 4 字節(32位)。
數組與指針的區別是 C 語言進階的分水嶺。牢記:數組名本質是數組對象標識,僅在特定語境退化為指針。理解 &cc 的類型 int(*)[N],掌握 sizeof 的語境敏感性,才能避開指針運算的陷阱,寫出健壯高效的 C 代碼。