后續(xù)文章也同時(shí)遷移至個(gè)人博客 http://kimihe.com/
參考: iOS 最詳細(xì)的解析(數(shù)組與指針)筆試題,并做了改進(jìn)。
引題
先來看一下這道題目,如下代碼的輸出結(jié)果是什么?
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));
在看后文的解釋前,不妨自己思考一下。
.
.
.
.
.
.
.
.
.
.
.
.
.
好了,相信你已經(jīng)思考過了,我們來揭曉結(jié)果,結(jié)果是令人匪夷所思的458758,答對(duì)了嗎?也許你會(huì)有疑惑,那么請(qǐng)看下面的注解,在其中增加了些許解釋代碼,并在關(guān)鍵代碼后有注釋。
詳細(xì)注釋解答:
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]先轉(zhuǎn)short*,再移動(dòng)2個(gè)short*位(4B)
short *sp2 = (short *)(&arrayName+2);//short(*)[10]先移動(dòng)2個(gè)數(shù)組位(20個(gè)short*: 40B->0x28),再轉(zhuǎn)short*型
printf("sp1:%p\nsp2:%p\n", sp1, sp2);
short *p = (short *)(&arrayName+1);//short(*)[10]先移動(dòng)1個(gè)數(shù)組位(10個(gè)short*: 20B->0x14),再轉(zhuǎn)short*型
int *q = (int *)(&arrayName+1);//short(*)[10]先移動(dòng)1個(gè)數(shù)組位(10個(gè)short*: 20B->0x14),再轉(zhuǎn)int*型
printf("p: %p\nq: %p\n", p, q);
printf("p-2:%p\nq-2:%p\n", p-2, q-2);
//q-2:移動(dòng)2個(gè)short*(4B), p-2:移動(dòng)2個(gè)int*(8B)
//x86是小端字節(jié)序: 0x0007 0006 -> 7*256*256+6=458758
printf("*(p-2):%d\n*(q-2):%d", *(p-2), *(q-2));
內(nèi)存地址分配圖:

內(nèi)存地址分配圖.png
重點(diǎn):
- 字節(jié)序
- 內(nèi)存地址分配
- 類型轉(zhuǎn)換
補(bǔ)充
最后我們通過匯編,來深入理解一下如下幾行代碼的含義:
int *p = NULL;
p = (int *)&arrayName;
p = (int *)(&arrayName+1);
對(duì)應(yīng)的匯編分別為:

匯編代碼.png
如圖三個(gè)紅圈,這邊是AT&T的匯編格式,我們還是以更好看的intel的樣式來說明:
- 第一個(gè)紅圈,左邊的
$0x00立即數(shù)賦給[rbp-0x28]這個(gè)內(nèi)存地址,0x00就是NULL的值,而[rbp-0x28]內(nèi)存地址就是指針p的值(不是*p)。 - 第二個(gè)紅圈,
rcx寄存器中含有arrayName數(shù)組的首地址(注意:arrayName+0和&arrayName取得的地址是相同的),賦給rdx;rdx在把它賦給[rbp-0x28],即賦給指針p。 - 第三個(gè)紅圈,第一句中,
$0x14立即數(shù)就是十進(jìn)制的20,表明10個(gè)short*型,因?yàn)槊總€(gè)short *占2B。$0x14賦給rcx寄存器,rcx再賦給指針p,最終導(dǎo)致前文所說的,所謂移動(dòng)整個(gè)數(shù)組位,從第0個(gè)元素移到了第11個(gè)元素(雖然數(shù)組并沒有第11個(gè)元素,但位置的移動(dòng)可以這樣理解)。