数组是一种构造数据类型。不同于指针,数组是在栈上静态分配空间,空间上是连续的,因为是静态分配空间,所以数组空间在定义时就分配空间,空间大小不能改变,造成空间利用率低的问题,为解决这一问题引入了可变长数组和柔性数组。数组按照维度可分为有一维数组、二维数组、三维数组等数组,数组的使用要注意 数组的定义、数组的初始化、数组名和数组地址所代表的的含义、数组名在当实参时,形参的类型,注意类型兼容问题。区分指针数组、数组指针、的使用和应用场景,最后还要区分指针和数组在空间分配、访问效率、安全性以及函数形参等方面的区别和联系。
C89标准:定义必须确定数组长度
int a [100];
静态分配不灵活,怎么解决数组长度不灵活的问题———可变长数组(C99标准中)&柔性数组
(1)可变长数组
实现原理:用变量定义数组大小
并不能真正解决数组长度问题 ,同时如果变量没有初始化,会随机赋值,当变量为负时,会出错
(2)柔性数组
int a[];
a | 指针常量,数组名不能自加自减,保存的是数组首元素地址,步长为单个元素步长 |
&a | 对数组名取地址表示数组的地址,步长为整个数组元素所占大小的步长 |
*(&a) == a | 对一维数组的地址取值等于数组首元素的地址 |
int aa[][];
aa | 指针常量,保存一维数组的地址 |
&aa | 二维数组的地址 |
*(&aa)= aa | 对二维数组的地址取值等于二维数组中首个一维数组的地址 |
*aa | 二维数组中首个一维数组首个元素的地址 |
int aaa[2][2][2];
1、定义和初始化
2、表达式说明:
aaa | 三维数组名,指针常量,保存三维数组中首个二维数组地址 |
*aaa | 首个二维数组中首个一维数组地址 |
**aaa | 首个二维数组中首个一维数组首个元素地址 |
***aaa | 首个二维数组中首个一维数组首个元素值 |
&aaa | 三维数组地址 |
*(&aaa)=aaa | 对三维数组地址取值等于首个二维数组的地址 |
5、 例: 分别用一维数组、二维数组、三维数组实现输入输出
#include
int main(int argc,char **argv)
{
char a[100];
printf("please input string\n");
scanf("%s",a);
printf("str1 = %s\n",a);
int b[2][2];
printf("please input number\n");
for(int i = 0;i<2;i++)
{
for(int j = 0;j < 2;j++)
{
scanf("%d",&b[i][j]);
}
}
printf("输出结果为\n");
for(int i = 0;i<2;i++)
{
for(int j = 0;j < 2;j++)
{
printf("b[%d][%d] = %d\n",i,j,b[i][j]);
}
}
int c[2][2][2];
printf("please input number\n");
for(int i= 0;i<2;i++)
{
for(int j = 0;j<2;j++)
{
for(int k = 0;k<2;k++)
{
scanf("%d",&c[i][j][k]);
}
}
}
printf("输出结果为\n");
for(int i= 0;i<2;i++)
{
for(int j = 0;j<2;j++)
{
for(int k = 0;k<2;k++)
{
printf("c[%d][%d][%d] = %d\n",i,j,k,*(*(*(c+i)+j)+k));
}
}
}
}
char *pa[3]
数组中保存的元素都是指针
1、注意:
a\没有被初始化的数组指针都是野指针,使用方法和指针一样,使用时要分配空间,使用完了要及时释放空间并置空
b\函数传参时,传递一维数组指针名,形参要用二维指针,因为传递一维数组名,用一维数组元素类型的指针,数组中的元素是指针,指针的指针是二维指针。
例:指针数组的定义和传参
#include
void print4(char **pa)
//注意是二维指针,传递数组名,形参是数组类型的指针,数组元素类型是指针,指针的指针不就是二维指针了吗
{
for(int i = 0;i<3;i++)
{
printf("pa[%d] = %s\n",i,pa[i]);
}
}
int main()
{
char *pa[3] ={"hello1","hello2","hello3"};
print4(pa);
}
1、概念
数组的地址用数组指针变量保存,或者说指针指向数组首元素地址
2、如何定义
int (* pa) [3];== int a[3];
pa == &a; *p == a;
注意:可以把 * 和 &看作 互为逆运算
3、数组指针使用场景——函数传参
#include
void print1(char *p)
{
printf("str1 = %s\n",p);
return;
}
void print2(char (*p)[100])
{
for(int i = 0;i<2;i++)
{
printf("str[%d] = %s\n",i,p[i]);//*(p + i)
}
return;
}
void print3(char (*p)[2][100])
{
for(int i= 0;i<2;i++)
{
for(int j = 0;j<2;j++)
{
printf("%s\n",p[i][j]);//*(*(p+i)+j)
}
}
return;
}
int main(int argc,char **argv)
{
char a[100];
printf("please input string\n");
scanf("%s",a);
print1(a);
char b[2][100];
printf("please input string\n");
for(int i = 0;i<2;i++)
{
scanf("%s",b[i]);//*(b + i)
}
printf("输出结果为\n");
print2(b);
char c[2][2][100];
printf("please input string\n");
for(int i= 0;i<2;i++)
{
for(int j = 0;j<2;j++)
{
scanf("%s",c[i][j]);//*(*(c+i)+j)
}
}
printf("输出结果为\n");
print3(c);
return 0;
}
(1)传递实参为一维数组名,形参用用元素类型的指针接
a为一维字符数组名,print1为调用函数名
实参: print1(a)形参:void print1(char * str)
(2)传递实参为二维数组名,形参用一维数组指针接(相当于二维指针)
print2(aa) char (*a)[]
(3)传递实参为三维数组名,实参用二维数组指针接(相当于三维指针)
print2(aaa) char (*a)[][]
补: 数组在做形参时,编译器会把数组会退化成指针,这样能提高效率
1、一维数组回退化成一维数组元素类型指针(一维指针);2、二维数组会退化成指向一维数组的指针(二维指针);3、三维数组回退化成指向二维数组元素的指针(三维指针)
指针数组与数组指针详解_men_wen的博客-CSDN博客_指针数组
数组是静态分配空间(栈),空间是连续的,定义即分配空间,访问效率快,存在空间长度固定的问题,不能更改,易产生空间利用率低,造成空间浪费。指针则是动态分配,用链表实现,空间长度可根据需求改变需要用户人为分配空间(堆上),使用完要及时释放空间,防止内存泄漏。
数组连续访问快,指针灵活性好,用来随机访问数据
数组有系统管理空间,安全性好,指针空间是用户管理,需要自己分配、释放,容易造成内存泄漏,安全性差。
数组在做形参时,编译器会把数组会退化成指针,这样能提高效率
1、一维数组回退化成一维数组元素类型指针(一维指针);2、二维数组会退化成指向一维数组的指针(二维指针);3、三维数组回退化成指向二维数组元素的指针(三维指针)
https://hanshuliang.blog.csdn.net/article/details/121637890