C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

时间:2022-09-06 08:46:47

前提:一维数组和一维指针为什么可以替换使用?

    int a[3] = { 1, 2, 3 };
    int *p = a;
    for (int i = 0; i < 3; i++)
        printf("%d ", *(p + i));

C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

上面测试表示可以相互替换使用

C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

printf("%p %p, %p", a, &a[0],p);

C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

a是数组名,在数组中代表了数组首地址,类似于&a[0]。
而int *p是一个int类型指针,也是指向每一个地址,所以两者的类型相同,都是代表int类型字节地址。
int *p = a;是正确的。
我们再使用*(p+1),就是将指针P所指向的位置加上一个int类型字节(4),正好到达了a[1]的数据地址。所以这种使用方法是正确的。

一:二维数组的数组名代表了什么?

int a[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

这里数组名,还是整个数组的首地址,也可以看做第一行的首地址,还可以看做第一行第一列元素的首地址
printf("%p %p %p\n", a, &a[0], &a[0][0]);

C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

要是我们想要获取每一行的地址呢?

其实我们上面就使用了&a[0]获取了第1行的首地址
printf("%p %p\n",&a[1], &a[1][0]);  //测试第二行的地址

C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

注意:

数组名a是一个地址,无论是几维数组。都是数组的首地址

二:二级指针又代表了什么?

对于一级指针:

每次谈到指针,想到的就是一个带有门牌号的钥匙,我们根据门牌号,才能找到对应的房间,才可以进去拿东西。

对于二级指针理解可以相同:

我们考虑,家里有个书房。书房要是在客厅。
那么我们现在人在外面,要回去去一本小黄书呵...
我们先要根据我们手中的要是找到房间号,开门进去,获取到第二把钥匙,查看标签是书房的,我们就可以使用这把要是去开书房的门,获取书籍来学习
我们可以看做:最靠近目标空间的那把钥匙是一级指针,然后再远一点的那把钥匙就是二级指针,以此类推...就是多级指针了
就如同上面钥匙和门牌号一样,指针和地址也是密不可分的。指针变量中存放的就是地址
除了一级指针可以直接获取到数据,其他级别的指针都是指向上一级指针的存储地址。我们可以根据地址,一级一级直到获取到一级指针就可以获取到数据了,使用*就可以开门获取数据了

*星号的理解

int* p;        //这是声明了一个一级指针变量p,p是一个地址
*p;            //*p就是去这个地址中获取数据



int **p        //这个P,也是一个指针变量,而且是一个二级,内部存放的也是一个地址
*p              //就是使用二级地址去获取了其中内容(内容是一级指针的地址)
*(*p)         //*p先获取了一级指针的地址,*(*p)就是根据一级指针地址去获取数据

二:二级指针和二维数组的错误用法

int a[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
int **p=a;

C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

或许你因为这只是一个警告而觉得无所谓。但是当你使用的时候,就会出现错误,崩溃

原因解析:

p是一个二级指针,p变量存放地址。a代表了数组的首地址。虽然间接级别不同,但是地址赋给地址,也没有啥错,所以只是警告

但是当我们试图使用二级指针时:

num = *(*(p+0) + 0);
我们是想用*(p+0)获取第一行的地址,然后使用*(*(p+0)+0)获取第一行第一列的地址

但是会报错

 C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

原因解析:

C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

从上一个原因解析我们知道,是将a的地址赋给了二级指针p. 所以a=0x0028fce0 p=0x0028fce0 当我们使用*(p+0)==*p试图去获取一级指针地址时,结果发现地址0x0028fce0下存放的不是一个地址,而是1,
那么只能强制转换1为地址0x00000001,所以*p=0x00000001。当我们再去尝试访问这块地址时*(*(p+0)+0)==**p,这不是我们能访问的,所以报错

三:正确的使用指针和二维数组

(一)int(*p)[3] = a;

    int a[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
    int(*p)[3] = a;    //指针变量p指向包含3个整型元素的一维数组
    int num;
    printf("%p %p %p\n", p, p+1, p+2);
    num = *(*(p + 1) + 1);  //指向第二行第二个
    printf("%d\n", num);

C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

指针变量p指向包含3个整型元素的一维数组,所以p的步长是其内部数据字节长度,所以p+1就是7B4-7A8=C===12就是含有3个整型元素的一维数组长度
int(*p)[3] = a;    
p+1==a[1]
p+2==a[2]

(二)int *p=a;//根据指针寻址是按照步长

int *p=a;将a的地址赋给p
而p是一个int* 指针,所以他的步长就是int类型4字节一步长。
因为数组在内存中的数据存储时连续的,所以可以使用一级指针寻址获取所有的数据
    int a[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
    int *p = a;
    for (int i = 0; i < 9;i++)
        printf("%d ", *p++);  //p就是一个地址,每次按照步长增加四

C语言复习---二维数组和二级指针的关系:没关系,别瞎想(重点)

四:正确使用二级指针

    int **arr = (int **)malloc(n*sizeof(int*));
    for (int i = 0; i < n; i++)
    {
        arr[i] = (int *)malloc(n*sizeof(int));
        memset(arr[i], 0, n*sizeof(int));
    }

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
            printf("%2d", arr[i][j]);
        printf("\n");
    }