关于指针数组与数组指针的理解

上一篇介绍了指针和数组的区别和联系,相信很多同学都已经明白了指针和数组的用法,那么如果指针和数组混合起来,你还会用吗?

指针数组

指针数组是一个数组,它里面存储的是指针变量。比如说 int *p[5],数组里面有五个元素,里面存储的是指针。由于[]*的优先级要高,故 p 先与[]结合,也就是说 p 是一个数组。

数组指针

数组指针是一个指针,它在32位机器下占四个字节大小,64位机器下占据八个字节。比如int (*p)[5],表示指向拥有5个元素的数组,示例:

int main()
{
    int a[3][5] = {0};
    int (*p)[5] = a;
}

需要注意的是,数组指针后面括号里面的数字必须与二维数组的列数相同才可以。

地址偏移量

地址偏移量的概念不太好理解,为了便于理解,我们引入房间地址楼层地址这两个概念。

在一维数组中,比如int a[5]; a 代表的是一个房间地址,那么 a+1 就会偏移一个房间地址的距离(以数组中的一个元素所占内存字节为单位进行地址偏移),也就是偏移到 a[1] 的地方。

&a 代表的是一个楼层地址&a+1 会偏移整个数组的长度(以整个数组所占内存字节为单位进行偏移),也就是偏移到了整个数组后面的地方。

指针数组操作二维数组

int main()
{
    //指针数组操作二维数组
    int a[4][5]; //定义一个二维数组
    int i, j;
    int *p[4]; //定义一个指针数组(是个数组),和二维数组的行数相同。

    //初始化二维数组
    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            a[i][j] = 10 * i + j;
        }
    }

    //输出二维数组内容
    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("a[%d][%d]= %d\t", i, j, a[i][j]);
        }
    }
    printf("\n\n");

    p[0] = *a; //指针数组(数组),存储的是"房间"的地址,而不是"楼层"的地址,如果这样写p[0] = a;这样在c++中编译不通过的
    p[1] = *(a + 1); //给第二个元素赋值
    p[2] = *(a + 2); //给第三个元素赋值
    p[3] = *(a + 3); //给第四个元素赋值

    //第一种方式输出二维数组内容
    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("p[%d][%d]= %d\t", i, j, p[i][j]);
        }
    }
    printf("\n\n");


    //第二种方式输出二维数组内容
    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("*(*(p+%d)+%d)= %d\t",i ,j, *(*(p + i) + j));
         }
    }
    printf("\n\n");

    //第三种方式输出二维数组内容
    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("*(p[%d]+%d)= %d\t", i, j, *(p[i] + j));
        }
    }
    return 0;
}

输出结果:


指针数组运行结果

解释:
p 数组里面存放的是一级指针,p[0] 保存的是二维数组第一层楼第一个房间地址,p[1] 保存的是二维数组第二层楼的第一个房间地址,p[2] 保存的是第三层楼的第一个房间地址,p[3] 保存的是二维数组第四层楼的第一个房间地址;之后,p[0][0] 表示:(p[0])[0] == (p 数组的第一个元素)[0] == (*a)[0] == (a[0])[0] == a[0][0],其他的以此类推,得出结果。

数组指针操作二维数组

int main()
{
    //数组指针操作二维数组
    int a[4][5]; //定义一个二维数组
    int i, j;
    int (*p)[5]; //定义一个数组指针(是个指针),和二维数组的行数相同

    //初始化二维数组
    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            a[i][j] = 10 * i + j;
         }
    }

    //输出二维数组内容
    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("a[%d][%d]= %d\t", i, j, a[i][j]);
        }
    }
    printf("\n\n");

    p = a; //让指针指向二维数组

    // p = *a; //虽然a和*a的值都是一样的,但是这样无法赋值(a是"楼层地址",*a是"房间地址")

    //第一种方式输出二维数组内容
    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("p[%d][%d]= %d\t", i, j, p[i][j]);
        }
    }
    printf("\n\n");


    //第二种方式输出二维数组内容
    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("*(*(p+%d)+%d)= %d\t",i, j, *(*(p + i) + j));
        }
    }
    printf("\n\n");

    //第三种方式输出二维数组内容
    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("*(p[%d]+%d)= %d\t",i, j, *(p[i] + j));
        }
    }
    return 0;
}

输出结果:


数组指针运行结果

解释:
p 里面存放的是一维数组的地址(注意不是数组首元素的地址,是整个数组的地址),在程序中 a 表示的就是第一层楼的地址,也就是 &a[0] 。由于 p 指向的是楼层地址,则 p+1 则表示第二层楼的地址,p+2 表示第三层楼的地址...当我们用 p[0][0] 的时候,其实表示的就是第一层楼的第一个房间内容(p 表示的第一层楼的地址,p[0] 表示的第一层楼的第一个房间的地址,p[0][0] 表示的第一层楼的第一个房间的内容),其他的以此类推。

总结

  1. 指针数组是数组,数组指针是指针;它们符合各自数组或指针的特性;
  2. 数组和指针都是操作地址的运算;
  3. 地址也是有级别的,例如上面所说的楼层地址房间地址(本质是地址操作时候的偏移单位不同);
  4. 很重要的一点,数组的地址是连续的!!上面的操作都是基于这点才可以实现的,如果换成链表,则上面程序的输出结果将无法判断;
  5. 上面提到的指针数组和数组指针都属于二级指针范畴,如果是int ****p[5],你还能操作二维数组吗?换成三维数组呢?抓住本质,无论多少级都一样操作。

补充

与指针数组和数组指针类似的还有一个:二级指针,示例:

int main() 
{
    int a[4][5]; 
    int i, j;
    int *q[4]; 
    int **p;

    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            a[i][j] = 10 * i + j;
        }
    }

    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("a[%d][%d]= %d\t", i, j, a[i][j]);
        }
    }
    printf("\n\n");

    q[0] = *a; 
    q[1] = *(a + 1); 
    q[2] = *(a + 2); 
    q[3] = *(a + 3); 
    p = q;

    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("p[%d][%d]= %d\t", i, j, p[i][j]);
        }
    }
    printf("\n\n");


    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("*(*(p+%d)+%d)= %d\t",i ,j, *(*(p + i) + j));
        }
    }
    printf("\n\n");

    for(i = 0; i < 4; i++) 
    {
        for(j = 0; j < 5; j++)
        {
            printf("*(p[%d]+%d)= %d\t", i, j, *(p[i] + j));
        }
    }
    return 0;
}

自己输出一下结果,尝试理解一下指针的工作原理,相信你可以征服它!!!

练习

int a[] = {0, 2,4,6,8};
int *p[5]={a, a+1, a+2, a+3, a+4};
int ** pp = p;
int main()
{
    printf("%d\n",*pp++);
    printf("%d\n", *pp-a);
    printf("%d\n", ** ++ pp);
    printf("%d\n", pp - p);
    return 0;
}

输出结果是什么呢?

#include <stdio.h>

int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    int *ptr = (int *)(&a + 1);
    printf("%d %d\n", *(a+1), *(ptr-1));
    return 0;
}

输出结果是什么呢?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,479评论 3 44
  • 变量的声明和定义变量声明(declaration) 可以declaration很多次,不占内存空间,例如 exte...
    FlyingReganMian阅读 1,056评论 0 1
  • 前言 最近真的是忙的不可开交,公司一直给安排任务,连学习和写笔记的时间都没有了,落下好几次课的笔记都没有写,所以我...
    Xiho丶阅读 1,545评论 1 12
  • 文/阿霞 忙 早上7点,园区的饭堂已经热闹起...
    深圳阿霞阅读 550评论 2 5
  • 从昨天回了家以后,接俩个电话,录了一个电台。我都没有说过一句话。这算我活了这么多年第一次感到绝望吧。夜很漫长,我睡...
    Joey冰阅读 403评论 2 5