陣列退化為指標
初學者常以為在 C 裡陣列和指標是同一種東西。它們不是——但在大多數運算式中,陣列會悄悄變成「指向其第一個元素的指標」,這個自動轉換就叫做陣列退化為指標(array-to-pointer decay)。這正是為什麼你能把陣列「以指標方式」傳給函式,卻不必在任何地方寫 &。
像 int a[10] 這樣的陣列是單一個物件:十個並排的 int,而名字 a 指稱整塊。一旦你在幾乎任何運算式中用到 a——a + 1、*a、把 a 傳給函式——它就退化成 int *,一個指向 a[0] 的指標。所以當你宣告 void f(int a[]) 時,那個參數其實是 int *a;陣列性質在邊界處就遺失了,在 f 內部 a 只是個指標。退化「不會」發生的兩個著名例外是:sizeof(a),它給出整個陣列的大小(十個 int 是 40 位元組);以及 &a,它給出整個陣列的位址,型別為 int (*)[10]——一個指向陣列的指標,而非指向 int 的指標。
退化是一個經典陷阱的源頭:sizeof 在陣列上和在指標上的行為不同。在一個收到退化後陣列的函式內部,sizeof(a) 是指標的大小(8 位元組),不是陣列的大小——長度沒能在退化中存活。這就是為什麼接受陣列的 C 函式幾乎總是另外帶一個長度參數;陣列一旦變成指標,就無法告訴你它有多長。
int a[10]; size_t s1 = sizeof(a); ——s1 是 40。但 void f(int a[]) { size_t s2 = sizeof(a); }——在 f 內部 s2 是 8(一個指標),因為 a 在呼叫時退化了。長度 10 不見了。
對陣列做 sizeof 得到 40;對退化後的指標做 sizeof 得到 8。
陣列名稱不是一個「持有指標」的變數——沒有一個可被指派的獨立指標物件;對兩個陣列做 a = b 是錯誤。退化是在運算式中產生一個暫時的指標值。因為長度無法在退化中存活,請永遠傳遞並信任明確的長度,絕不要對參數使用 sizeof。