后续文章也同时迁移至个人博客 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));
内存地址分配图:
重点:
- 字节序
- 内存地址分配
- 类型转换
补充
最后我们通过汇编,来深入理解一下如下几行代码的含义:
int *p = NULL;
p = (int *)&arrayName;
p = (int *)(&arrayName+1);
对应的汇编分别为:
如图三个红圈,这边是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个元素,但位置的移动可以这样理解)。