后續文章也同時遷移至個人博客 http://kimihe.com/
參考: iOS 最詳細的解析(數組與指針)筆試題,并做了改進。
引題
先來看一下這道題目,如下代碼的輸出結果是什么?
short arrayName[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *q = (int *)(&arrayName+1);
printf("*(q-2):%d\n", *(q-2));
在看后文的解釋前,不妨自己思考一下。
.
.
.
.
.
.
.
.
.
.
.
.
.
好了,相信你已經思考過了,我們來揭曉結果,結果是令人匪夷所思的458758,答對了嗎?也許你會有疑惑,那么請看下面的注解,在其中增加了些許解釋代碼,并在關鍵代碼后有注釋。
詳細注釋解答:
short arrayName[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int i = 0; i < 10; i++) {
printf("arr%d addr:%p\n", i, arrayName+i);
}
//&arratName是short(*)[10]類型
short *sp1 = (short *)&arrayName+2; //short(*)[10]先轉short*,再移動2個short*位(4B)
short *sp2 = (short *)(&arrayName+2);//short(*)[10]先移動2個數組位(20個short*: 40B->0x28),再轉short*型
printf("sp1:%p\nsp2:%p\n", sp1, sp2);
short *p = (short *)(&arrayName+1);//short(*)[10]先移動1個數組位(10個short*: 20B->0x14),再轉short*型
int *q = (int *)(&arrayName+1);//short(*)[10]先移動1個數組位(10個short*: 20B->0x14),再轉int*型
printf("p: %p\nq: %p\n", p, q);
printf("p-2:%p\nq-2:%p\n", p-2, q-2);
//q-2:移動2個short*(4B), p-2:移動2個int*(8B)
//x86是小端字節序: 0x0007 0006 -> 7*256*256+6=458758
printf("*(p-2):%d\n*(q-2):%d", *(p-2), *(q-2));
內存地址分配圖:
內存地址分配圖.png
重點:
- 字節序
- 內存地址分配
- 類型轉換
補充
最后我們通過匯編,來深入理解一下如下幾行代碼的含義:
int *p = NULL;
p = (int *)&arrayName;
p = (int *)(&arrayName+1);
對應的匯編分別為:
匯編代碼.png
如圖三個紅圈,這邊是AT&T的匯編格式,我們還是以更好看的intel的樣式來說明:
- 第一個紅圈,左邊的
$0x00
立即數賦給[rbp-0x28]
這個內存地址,0x00
就是NULL
的值,而[rbp-0x28]
內存地址就是指針p
的值(不是*p
)。 - 第二個紅圈,
rcx
寄存器中含有arrayName
數組的首地址(注意:arrayName+0
和&arrayName
取得的地址是相同的),賦給rdx
;rdx
在把它賦給[rbp-0x28]
,即賦給指針p
。 - 第三個紅圈,第一句中,
$0x14
立即數就是十進制的20,表明10個short*
型,因為每個short *
占2B。$0x14
賦給rcx
寄存器,rcx
再賦給指針p
,最終導致前文所說的,所謂移動整個數組位,從第0個元素移到了第11個元素(雖然數組并沒有第11個元素,但位置的移動可以這樣理解)。