数组与指针

天气逐渐转冷了,各位IT界的朋友,注意保暖,爱惜自己身体~~

最近在浏览某篇博客的时候,看到了大概两年前看到过的一个问题,是一个面试题,因为两年前那时候没有搞懂,所以当时也没有过多地纠结,最近再次看到这个问题,颇有感慨,因此,就结合自己的理解,简单总结一下。

面试题大概是这样:

请问下面这段代码的打印结果:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        int numbers[4] = {10, 20, 30, 40};
        int *p = (int *)(&numbers + 1);
        NSLog(@"%d", *(p - 1));

}

换作在两年前,我看到这种问题,连吃午饭的胃口都没有了。。。。
但是现在再重新来看这个问题,虽然需要经过一点时间的思考,但还是可以得出正确结果为40。

下面咱们就一步一步来梳理一下。

  • 首先来看
int numbers[4] = {10, 20, 30, 40};

上面这句代码表示:numbers数组装有4个int类型的元素。由于一个int类型在内存中占4个字节,因此,numbers数组在内存中总共占有16个字节。

  • 继续来看
NSLog(@"%p", numbers); // 这种写法是在获取 数组名
NSLog(@"%p", &numbers); // 这种写法是在获取整个数组的首地址
NSLog(@"%p", &numbers[0]); // 这种写法是在获取 数组中首元素的首地址

上面三句代码分别取得数组名、数组的内存地址、数组中第一个元素的内存地址。这三者其实是同一个值,因为数组的第一个元素的内存地址就代表数组的内存地址。

  • 紧接着,咱们来定义一个指针
int *p; 

上面这行代码,代表p是指向int类型的数据的指针。
现在,咱们将数组中的第一个元素的内存地址赋值给它

p = &numbers[0];

这样,指针p就指向了数组中第一个元素的内存地址。即指向第一个元素所占用的4个字节(因为int为4个字节)。
但是,这里p只会存储4个字节中的第一个字节(下面的打印就可以验证),这就是int的意义所在,因为已经提前声明了p是指向int类型的数据,因此只要根据p存储的那个字节,依次按序往下再算3个字节,那么这4个字节就是p所指向的这个数据所占用的内存。同理double、float、long。

  • 打印一下p、p + 1对应的地址
NSLog(@"%p", p);     // 0x7ffeefbff590
NSLog(@"%p", p + 1); // 0x7ffeefbff594

结果显示,p + 1增加了4。一个简单的结论:指针数据 + 1,表示当前指针指向的地址值 + 指向类型所占用的字节数
咱们再来打印一下numbers和numbers + 1,看看得到的结果是否遵循咱们得出的结论:

NSLog(@"%p", &numbers); // 0x7ffeee086a70
NSLog(@"%p", &numbers + 1); // 0x7ffeee086a80

打印结果显示,&numbers + 1的内存地址比&numbers的内存地址增加了16(16进制),正好是numbers数组所占用的字节数(4个int类型的元素)。由此可以确认咱们的结论是正确的。(这里有一个常识:&a就代表一个指针。上面用到的&numbers[0]其实就是一个指针,只不过为了简单化,所以用p代替了。)
因此,咱们可以推出类似于公式的简单算法:
指针p + n 表示 p指向的地址值 + n * p指向的类型所占用的字节数
指针p - n 表示 p指向的地址值 - n * p指向的类型所占用的字节数

  • 接下来,咱们来看下面这种写法:
NSLog(@"%d", *p); // 10

上面这种写法,结果为指针p指向的数据的值。由于上面设置p = &numbers[0];因此,上面这种写法获取的结果 下面这种写法获得的值:

NSLog(@"%d", *(&numbers[0])); // 10

由于上面说到&numbers&numbers[0]一样,都是第一个元素的内存地址,因此,下面这种写法获取到的值也同上面两种是一样的:

NSLog(@"%d", *numbers); // 10

此时,咱们再回到文章一开始给出的面试题,一步一步来看:

  • &numbers + 1:numbers数组的内存地址增加了16。
  • (int *)(&numbers + 1):强转为int类型的指针。
  • int *p = (int *)(&numbers + 1):将这个指针赋值给了p。

这样,指针p所指向的的内存地址就是numbers数组的内存地址增加16,也就是numbers[0]这个元素对应的内存地址增加16。
因为p是int类型,此时,p - 1就意味着p指向的内存地址减去4个字节,而且结果正好是numbers[3]元素对应的内存地址,因此NSLog(@"%d", *(p - 1));得到的结果就是numbers[3]对应的值,也就是40。

你可能感兴趣的:(数组与指针)