一次性搞清数组指针、指针数组---从多维数组的本质上刨析(让你顿悟版)

简介: 一次性搞清数组指针、指针数组---从多维数组的本质上刨析(让你顿悟版)

想要彻底理解:建立在知道如何画内存四区图的基础上,如果不是特别清楚什么是内存四区,建议先看下这篇文章:变量的本质分析、内存四区、函数调用模型_睡不饱的小默的博客-CSDN博客

在讲解之前:我们先聊下什么是数组类型,不然后面很难搞懂数组指针。

数组类型:数组的类型由元素类型和数组大小共同决定的,也就是说int[5]、int[6]、char[5]是三种不同的数组类型。(int array[5]的类型为int[5])

1. //定义数组类型,并用int为元素类型
2. 
3. typedef int(MYINT5)[5];
4. 
5. int main()
6. {
7.     MYINT array;    //定义了一个元素类型为int,大小为5的数组类型
8. 
9. for(int i=0; i<5;i++)
10.     {
11.         array[i] = i;
12. printf("array[%d]:%d\n",i,array[i]);
13.     }
14. 
15. 
16. return 0;
17. }

好,现在我们搞清楚了什么是数组类型。

接下来我们进入正题:什么是数组指针和指针数组?

数组指针

定义:指向数组的指针

补充一个知识,方便我们更容易的理解什么叫指向数组:

举例:int a[10]

数组名是数组首元素的起始地址,但不是数组的起始地址 。[a]

那什么才是数组的起始地址呢?【&a】

1. //我们可以通过以下程序进行验证
2. 
3. int main()
4. {
5.     int a[10];
6. printf("a:%d \n",a);
7. printf("&a:%d \n,&a);
8. 
9. //a = &a-->因为他们的值都等于 &a[0]
10. //a实际上与&a[0]等效
11. 
12. //我们分别将他们加1
13. printf("a+1:%d \n",a+1);
14. printf("&a+1:%d \n",&a+1);
15. 
16. //发现a+1对于a,增加了  4
17. //而&a+1,对于&a增加了 40
18. //--->通过上述例子可以更好的理解什么是数组类型了,哈哈
19. 
20. }

假设数组指针p是一个指向int a[10]的数组类型,很明显p+1与p相差了40

那我们如何定义一个数组指针呢

int *p = a;  ? ---->那么p+1与显然只相隔了 4

int *p = &a;   ---->报错,起始&a相当与一个二级指针

//定义数组类型,并用int为元素类型
typedef int(MYINT5)[5];
int main()
{
    MYINT array;    //定义了一个元素类型为int,大小为5的数组类型
    for(int i=0; i<5;i++)
    {
        array[i] = i;
        printf("array[%d]:%d\n",i,array[i]);
    }
    return 0;
}

指针数组

定义:一个由指针组成的数组。// int *p[];

废话不多说,直接上图:(如果看不懂下图:先看变量的本质分析、内存四区、函数调用模型_睡不饱的小默的博客-CSDN博客)

为了加深我们的理解:

接下来我们从多维数组的本质上进行刨析:

int myarray[3][5] 与 int (*p)[5]?

myarray===>到底代表了什么?

为什么数组 char a[i][j] ==>*(*(a+i)+j)?

先给出答案然后我们再进行案例测试:

myarray-->是一个数组指针,指向低维数组的指针,每次往后跳一维的长度(可以理解为一行)。

补充:为了方便理解,我们不妨将 [ ]和 *理解为降维(即取地址里的内容),&为升维

假设:a[3][5]

a         ---> &a[0]

a+1    ---> &a[1]

a+0  ----> &a[0]

*(a + 0) + 0--> &a[0][0]

*(*(a+0)+0) ---> a[0][0]

同理:*(*(a+i)+j) ---> a[i][j]

好的,我们用一段用例来测试一下:

//我们可以通过以下程序进行验证
int main()
{
    int a[10];
    printf("a:%d \n",a);
    printf("&a:%d \n,&a);
    //a = &a-->因为他们的值都等于 &a[0]
    //a实际上与&a[0]等效
    //我们分别将他们加1
    printf("a+1:%d \n",a+1);
    printf("&a+1:%d \n",&a+1);
    //发现a+1对于a,增加了  4
    //而&a+1,对于&a增加了 40
    //--->通过上述例子可以更好的理解什么是数组类型了,哈哈
}

引出:

多维数组做函数参数的退化的原因

//数组指针类型
/*
    数组指针--->用于指向一个数组的指针
    分清两个概念:数组名代表数组首元素的起始地址,但不是数组的起始地址 a
                                    通过将取地址符&作用于数组可以得到整个数组的起始地址 &a
        定义数组指针:
  1.通过数组类型定义数组指针
    typedef int(ArrayType)[5];
    ArrayType *pointer;
  2.声明数组指针类型
    typedef int(*MyPointer)[5];
    MyPoint myPoint;
  3.直接定义:
    int(*pointer)[5];
*/
int main()
{
  //  1.通过数组类型定义数组指针
  {
        int a[5];
        typedef int(ArrayType)[5];
        ArrayType *array;
        array = &a;
        for(int i =0 ;i<5;i++)
        {
            (*array)[i] = i;
            printf("array[%d]:%d\n",(*array)[i]);
        }
  }
//  2.声明数组指针类型
    {
        int a[5];
        typedef int(*MyPointer)[5];
        MyPointer mypointer;
        mypointer = &a;
        for(int i =0 ;i<5;i++)
        {
            (*mypointer)[i] = i;
            printf("mypointer[%d]:%d\n",(*mypointer)[i]);
        }
    }
    //3.直接定义:
    {
         int a[5];
         int(*Pointer)[5] = NULL;
        Pointer = &a;
        for(int i =0 ;i<5;i++)
        {
            (*Pointer)[i] = i;
            printf("Pointer[%d]:%d\n",(*Pointer)[i]);
        }
    }
        return 0;
}

为什么会有退化问题的存在:

      C/C++编译器只会机械的值拷贝的方式传递参数(实参把值传给形参,地址传递也不过是把地址的值传给了形参)。编译器在处理a[n]的时候,它没有办法知道n是几,它只知道&a[0],它是通过值传递进去的。-->这里就说明了一个东西,api函数的接口一般都是传地址加长度。

int fun(char a[20]) -->虽然函数能够得到20这个数字,但编译器没有这么做

典型的等价关系:

数组参数                                                                        等效的指针参数

char a[30]                                                                               char *

char *a[30]                                                                               char **

char a[10][30]                        

相关文章
|
1月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
38 3
|
27天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
1月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
1月前
|
容器
在使用指针数组进行动态内存分配时,如何避免内存泄漏
在使用指针数组进行动态内存分配时,避免内存泄漏的关键在于确保每个分配的内存块都能被正确释放。具体做法包括:1. 分配后立即检查是否成功;2. 使用完成后及时释放内存;3. 避免重复释放同一内存地址;4. 尽量使用智能指针或容器类管理内存。
|
1月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
1月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
57 4
|
1月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
48 2
|
1月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
39 1
|
2月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
2月前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。

热门文章

最新文章