1.一维数组向指针转换
先给出几个定义:
int a [10] :即是说明 a 是一个连续的内存块,有10个结构,这个结构被看成 int 去解析。
int * b = a;即是说明 b 是一个内存块,有 1 个结构,这个结构被看成一个 int* (指向 int 地址)去解析。即告诉编译器,要把 b 里面存放的数值当成一个地址去解析。
先说明这两者的不同及相同。
前者是一个数组,它是编译器的天生支持数据结构,只要这样写,就等于告诉编译器,我需要的是一个数组,然后编译器就会安排内存,并且返回一个你可以操纵整个数组的入口地址,即将这个内存块的首地址返回给 a 去接受。其实也可以不用返回首地址,也可以返回尾地址什么的,这是因为人最习惯的思维是加法,况且,从某种意味上来说,生成一个数组,是一个从无到有的过程,我们返回最起始的东西,也更符合人的思维方式。就好比,你去取钱,如果钱是连号的,那么,往往是号最小的钱放在上面。好了,扯了一些无关的话题,让我们回到正题。数组 a 的每一个元素是一个 int ,它占的内存空间是确定的,即 sizeof(int),当使用 a[2] 就是取第 3 个 int,这是自然语言,必须要转化为机器语言才能让机器处理,即,要把那个请求翻译为机器语言,前面说了,数组是连续的,(先不要考虑二维数组),那么,我们将数组的首地址向后面偏移 2 个 int 就正好得到第 3 个 int 的地址,也即:
a[2] = *(a + 2); //式1
等等,a 不是一个地址吗,地址的单位是字节,这个地址加上 2 字节,怎么会是将 a 向后偏移 2 个 int 的地址呢?按理说,这里应该是
a[2] = *(a + 2 * sizeof(int));//式2
是的,没错。但是编译器为了使上面的表达式看起来更加自然,它帮我们自动将式1转换为了式2,因而能够正确地寻址。我暂且把这个过程叫作“地址运算纠正”。
既然,a 就是数组的首地址,我们知道,地址在内存里面占的大小即刚好是机器位数,那么它就刚好与 int 占的位数相等,因此,我们可以用一个 int 类型的变量去接受这个地址,即:
int c = a;
此时,c 里面存放的就是数组的首地址,我们可以将式1改写如下: