一文看懂 二维数组、数组指针、指针数组、二级指针、数组名字、指针变量

我理解中的二维数组、数组指针、指针数组、二级指针:

1、二维数组: 人们为了便于理解,主观性认为存在的一个概念,从底层来看,二维数组仍然是一个一维数组,只不过是一位数组的每个元素仍然是一个一位数组,大方向上只要把它理解为一个一位数组,很多关于地址、指针的问题都很容易解决!

//定义并给二维数组赋值
int a[3][3]=
{
    {1,2,3}
    {4,5,6}
    {7,8,9}
}

二维数组必需知道的几个点:

①二维数组的名字a是数组的首地址,它默认是一个行指针,加1 保存下一行的首地址。
②行指针可以理解为二维数组的某个元素即其中的一维数组的首地址
③只不过在二维数组里需要通过*号使指针降级为列指针,加1保存下一个元素的地址,从而读取到二维数组里的每个一维数组里的值!(使用“&”符号可以使列指针升级为行指针)
④如果在函数中需要二维数组作为参数来传递,则使用数组指针(往下看,对比理解)
在这里插入图片描述
图中代码:

#include <stdio.h>
int main(void)
{
    int a[3][3]=
    {
        {1,2,3},
        {4,5,6},
        {7,8,9}
    };

    //数组首地址,是一个行指针,指向的是{1,2,3}这个数组的地址
    printf("数组名指向-->%p\n",a);

    // *号可以使地址降级,行指针变为列指针,但它仍然指向的是{1,2,3}这个数组的第一个元素的地址,注意是地址,不是值!
    printf("数组名默认是行指针,降级后指向-->%p\n",*a);
    
    //这才是a[0][0]的值!
    printf("对降级后的列指针取值=%d\n\n",**a);


	//行指针+1,跳一个一维数组,保存下一行的首地址
    printf("数组名+1操作后指向下一行的地址是-->%p\n",a+1);
    
    //行指针+1,并且使行指针降级,成为列指针,地址和行指针是一样的
    printf("数组名+1操作后指向下一行地址,再降级成为列指针指向-->%p\n",*(a+1));

	//获取第二行的第一个元素的值,即a[1][0]
    printf("对降级后的列指针取值=%d\n",*(*(a+1)));

    return 0;
}

2、数组指针:
定义一个数组指针变量p,p指向的是整型的有5个元素的数组,p+1 往下指5个整型,跳过一个有5个整型元素的数组。

int b[2][5]=
{
   {1,2,3,4,5},
   {6,7,8,9,10}
};
int (*p)[5];
p=b;

数组指针本质是一个指针,指向的是一个一维数组的地址,该地址和该一维数组的第一个元素的地址是一样的,数组指针加1跳一个数组,即保存下一个数组的地址,说白了数组指针就是上面二维数组中的行指针,上面说到,在函数参数列表中,传递二维数组需要使用数组指针就是这个原因,二维数组名本身默认是一个行指针,只要参数类型是数组指针,调用函数时把二维数组名放进去就可以了。
在这里插入图片描述
图中代码:

#include <stdio.h>
//通过数组指针形式传递二维数组,int(*p)[4],p指向整型的有4个元素的一维数组
void fun2( int (*p)[4] )
{
    //p[x][y] <==> *(*(p + x) + y)
    //p:默认是二维数组的行指针
    //p+x:跳过多少行,p默认是第0行
    //*(p+x):行指针降级为列指针,指向该行的第0个元素
    //(*(p+x)+y):移动列指针
    //*(*(p + x) + y):获取某行某列的具体数值
    printf("%d\n", p[0][2]);//3
    printf("%d\n", *(*(p+1) + 2));//7
}
void test2()
{
    int a[2][4] =
    {
        {1, 2, 3, 4},
        {5, 6, 7, 8}
    };
    fun2(a);
}
int main(void)
{
    test2();
    return 0;
}

3、指针数组:
本质是一个数组,当然可以是一维数组,也可以是二维数组,只不过数组中的每个元素都是指针类型的,是若干个相同类型的指针变量构成的集合。

int main(void)
{

    //大多数情况下,指针数组都用来保存多个字符串
    char *str[5] = {"hello","world","I","LOVE","C"};
    for(int i=0;i<5;i++)
    {
    printf("%s\n",str[i]);
    }
    return 0;
}

4、二级指针(指针的指针):
定义一个指针变量本身是个变量也会占有一定的空间,在32位平台下,地址总线是32位的,那么地址空间也是32位编号的,所以任何类型的指针变量都是4个字节大小。指针变量的作用是指向其他类型变量的空间,但是它自己也有地址编号,就会存在获取指针变量地址的需求,这就是二级指针,以此类推,如果你有需要获取二级指针的地址,那就需要三级指针(主要体现在函数传参的时候)

#include <stdio.h>
int main()
{
    int a = 100;
    //定义一个一级指针
    //一级指针用于保存普通变量的地址
    int *p = &a;
    //定义一个二级指针
    //二级指针用于保存一级指针的地址
    int **q = &p;
    printf("a = %d %d %d\n", a, *p, **q);//获取a的值
    printf("&a = %p %p %p\n", &a, p, *q);//获取a的地址
    printf("&p = %p %p\n", &p, q);//获取指向a的指针变量的地址
    printf("&q = %p\n", &q);//获取二级指针的地址,需定义三级指针来接收
    return 0;
}

运行结果:
在这里插入图片描述
二级指针的应用:地址传参 – 传地址
要求:如果实参是一个普通变量,地址传参的话就需要形参是一级指针,如果实参是一个一级指针,地址传参的话就需要形参是一个二级指针,以此类推。

#include <stdio.h>
//传指针数组
void fun3(char **q)
{
    int i;
    for(i=0;i<3;i++)
    {
        printf("%s\n",*(q+i));
    }
}

void test4()
{
    //一维指针数组本身是一个数组,数组名是一个地址,可以理解为一级指针
    //所以在函数形参中要用二级指针,即指针的指针,这才符合地址传参的要求
    char *p[] = {"hello","world","nihao"};
    fun3(p);
}

int main()
{
    test4();
    return 0;
}

运行结果:
在这里插入图片描述
5、数组名字和指针变量的区别:

int a[10];
int b[3][3];
int *p;
p=a;

相同点:

  • 对于一维数组:a是数组的名字,是a[0]的地址,p=a即p也保存了a[0]的地址,即a和p都指向a[0],所以在引用数组元素的时候,a和p等价!
  • 对于二维数组:b是数组的名字,是数组第一行的指针,它是指向一整行的,本质也是一个数组指针,*b是b[0] [0]的地址,**b才表示b[0] [0]的值

不同点:
1、 a是常量、p是变量,可以用等号’=’给p赋值,但是不能用等号给a赋值,更不能进行a++的操作。
2、 对a取地址,和对p取地址结果不同
因为a是数组的名字,所以对a取地址结果为数组指针
p是个指针变量,所以对p取地址(&p)结果为指针的指针(即二级指针)。

  • 6
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值