經典面試題:int a[5] = {1,2,3,4,5}; int *p1 = (int*)(&a+1); int *p2 = (int*)((int)a+1); 輸出 p1[-1] 和 *p2 分別是多少?本文從記憶體佈局、指針運算、類型轉換三個維度徹底講透。
假設 a 起始地址 0x100 (32位系統,int 4字節)
a[0] @0x100=1, a[1]@0x104=2, a[2]@0x108=3, a[3]@0x10C=4, a[4]@0x110=5
&a : 類型 int(*)[5],值 0x100
&a + 1 : 類型 int(*)[5],值 0x100 + 5*4 = 0x114 (陣列尾後)
(int*)(&a+1) : 強轉為 int*,指向 0x114
p1[-1] : *(p1 - 1) = *(0x114 - 4) = *0x110 = a[4] = 5 ✓
(int)a : 將陣列首地址 0x100 視為整數
(int)a+1 : 0x101 (地址+1字節)
(int*)((int)a+1) : 強轉為 int*,指向 0x101
*p2 : 讀取 0x101 起始 4 字節組成的 int (小端序)
內存: 0x100=01, 0x101=00, 0x102=00, 0x103=00 (a[0]=1)
0x101=00, 0x102=00, 0x103=00, 0x104=02 (a[1]=2 的低位)
組合: 0x02000000 = 33554432 (視具體機器碼而定)
p1[-1] = 5 *p2 = 33554432 (小端機器上,讀取 a[0] 低 3 字節 + a[1] 低 1 字節)
注意:*p2 屬於未定義行為 (違反嚴格別名規則、對齊要求),不同編譯器/優化等級結果可能不同,絕不可用於生產代碼。
| 表達式 | 類型 | 值(地址) | 解引用結果 |
|---|---|---|---|
&a | int(*)[5] | 陣列首地址 | 整個陣列 (不可解引用為 int) |
&a+1 | int(*)[5] | 陣列尾後地址 | 越界 |
(int*)(&a+1) | int* | 陣列尾後地址 | p1[-1] 合法訪問最後元素 |
(int)a | int (整數) | 首地址數值 | 非指針不可解引用 |
(int*)((int)a+1) | int* | 首地址+1字節 | **未定義行為** (對齊/別名違規) |
a 退化為 int* (首元素地址);&a 類型為 int(*)[5] (整個陣列地址),步長差異巨大。int* ±1 移動 4 字節;int(*)[5] ±1 移動 20 字節。(int*)integer 純粹重新解釋位址,編譯器不保護對齊、類型安全,**極度危險**。p[-1] 等價 *(p-1),只要結果地址合法即可,常用於「指向陣列尾後的指針倒序訪問」。