预说明:指针就是地址(eg:int*p=NULL)(NULL空指针)由指针变量(p)接收,但习惯性可能把指针变量直接称作指针,文中可能出现这种说法时,请有所了解。
一.数组的易错点
一维数组本质是一块连续的内存空间,用来存储同种数据类型的数据。
多维数组本质同样是一块连续的内存空间,用来存储同种数据类型的数据,空间不是以矩形排列(二维数组),或立方体排列(三维数组),以此类推。(图示如下,二维数组在内存中的空间)
如图,数组arr[3][3]在内存中不是图一的形式,而是图二,所以二维数组实际是一段连续的内存空间,多维数组可类比相同。(存储时前一行尾部连接下一行首部)
二.数组名的理解
一维数组(int a[3]={1,2,3};)
1.a是对内存空间的映射,用人话讲就是给一块空间起个名字。
2.a可看作一个指针常量,指向首元素,数值上固定表示a[0]的地址。
3.&a的值是a[0]的地址,但本质是数组的总地址,存在数组指针中(后文有对其的讲解)
(int(*p)[3]=&a)
#include<stdio.h>
int main()
{
int a[3]={1,2,3};
printf("%p %p %p\n",a,&a[0],&a);
printf("%p %p %p\n",a+1,&a[0]+1,&a+1);
return 0;
}
结果如图,上一行打印地址证明a,&a,&a[0]的值都是相同的,下一行指针加1,a+1多了4是多了数组中的一个元素的字节数,等于多了一个int类型所占的字节数,所以多了4(涉及指针加减的知识),&a[0]+1等于多了一个int类型的字节数,所以多4,&a+1是多了一个数组的总空间字节数,相当于4个int类型的空间,所以多了12。
二维数组(int b[3][3]={0};)
1.b是对内存空间的映射,用人话讲就是给一块空间起个名字。
2.b可看作一个指针常量,指向首元素,数值上固定表示b[0][0]的地址。
3.&b的值是b[0][0]的地址,但本质是数组的总地址,存在数组指针中(后文有对其的讲解)
(int(*p)[3][3]=&b)
#include<stdio.h>
int main()
{
int b[3][3]={0};
printf("%p %p %p\n",b,&b[0][0],&b);
printf("%p %p %p\n",b+1,&b[0][0]+1,&b+1);
return 0;
}
结果如图,上一行打印地址证明b,&b[0][0],&b的值都是相同的,下一行指针加1,b+1多了12是多了数组中的一个元素的字节数,二维数组一个元素为一行,等于多了3个int类型所占的字节数,所以多了12(涉及指针加减的知识),&b[0][0]+1等于多了一个int类型的字节数,所以多4,&b+1是多了一个数组的总空间字节数,相当于3*3=9个int类型的空间,所以多了36。(此处内容实际不全,最后补充中有详细代码)
多维数组
前文提到,数组名是指向数组首元素的指针常量,二维数组的一个元素表示一行,可以看作一个一维数组,同理,三维数组一个元素是一个二维数组,n维数组一个元素是(n-1)维数组,且各数组名是指向其数组首元素的指针,所以二维数组数组名是一个指向一维数组的指针(int (*b)[3]),以此类推,n维数组数组名是一个指向(n-1)维数组的指针。
三.数组指针和指针数组
数组指针
eg:int (*p)[3]是数组的指针,个人理解为一次开辟了3个int类型的空间大小,其值是首个字节的地址,可理解为首个int类型指针的地址,指针加1后地址增加3*4个值,在前面一维数组的代码中进行了测试。
指针数组
可理解为一种特殊的数组,只不过里面存的东西由int char等类型的数据变成了相应的地址。
#include<stdio.h>
int main()
{
int a=1,b=2,c=3;
int*pa=&a;
int*pb=&b;
int*pc=&c;
int *p[3]={pa,pb,pc};//也可将pa变成&a,都是地址
printf("%d %d %d",*(p[0]),*(p[1]),*p[2]);
return 0;//输出1 2 3
}
总结
int (*p)[3]为数组指针的格式(多维数组后加[n] (n为任意常量))
int *p[3]为指针数组的基本格式,多维数组同上解释
二者存在差异的原因是因为运算符优先级的问题。 () > [] > *
四.指针模拟动态开数组
malloc函数介绍
malloc函数:申请一段空间,并返回该空间首地址
头文件:<malloc.h>or<stdlib.h>(ANSI建议用后者)
int *p=(int*)malloc(size_t);
void(相应类型)*p=(返回类型)malloc(申请空间字节数);
开一维数组
#include<stdio.h>
#include<stdlib.h>
int main()
{
//开5个int类型大小的空间用来存数值
int*p=(int*)malloc(5*sizeof(int));
for(int i=0;i<5;i++)
{
p[i]=i;//赋值0-4
}
for(int i=0;i<5;i++)
{
printf("%d ",p[i]);//输出0-4
}
return 0;
}
开二维数组
#include<stdio.h>
#include<stdlib.h>
int main()
{
//开5个空间存5个一级指针
int**p=(int**)malloc(5*sizeof(int*));
//每个一级指针开5个空间用来存数组中的值
p[0]=(int*)malloc(5*sizeof(int));
p[1]=......
......//效果相当于int p[5][5];
return 0;
}
五. 二维数组 数组名的补充
#include<stdio.h>
int main() //为方便讲解,标上序号
{
int b[3][3]={0}; // 1 2 3 4 5
printf("%p %p %p %p %p\n",&b, b, &b[0], b[0], &b[0][0]);
printf("%p %p %p %p %p\n",&b+1,b+1,&b[0]+1,b[0]+1,&b[0][0]+1);
return 0;
}
第一行运行结果说明序号1-5的值都是相同的。
结合上文(二 . 二维数组的内容),可将其分为(1)(2 3)(4 5) 共三组,每一组的本质是相同的,这里不做细致讲解,请结合上文内容理解。