1.谈谈你对数组的理解
数组是一种构造数据类型。 与指针不同,数组在堆栈上静态分配空间,并且空间是连续的。 因为空间是静态分配的,所以数组空间是在定义时分配的。 空间大小无法改变,造成空间利用率低的问题。 为了解决这个问题引入了变长数组和灵活数组。 数组按照维数可以分为一维数组、二维数组、三维数组以及其他数组。 使用数组时,要注意数组的定义、数组的初始化、数组名和数组地址的含义,以及当数组名作为实参、形参的类型时,要注意类型兼容性问题。 区分指针数组、数组指针的用法和应用场景,最后区分指针和数组在空间分配、访问效率、安全性、函数参数等方面的区别和联系。
2. 如何使用数组 1. 定义数组的大小
C89标准:定义必须确定数组长度
整数a[100];
静态分配不灵活,如何解决数组长度不灵活的问题——变长数组(C99标准中)&灵活数组
(1) 变长数组
实现原理:使用变量定义数组大小
它并没有真正解决数组长度的问题。 同时,如果变量没有初始化,则会被随机赋值。 当变量为负数时,就会出现错误。
(2) 灵活阵列
2、一维数组是由元素组成的,组成的单位是元素。
整数a[];
指针常数。 数组名称不能递增或递减。 它存储数组第一个元素的地址。 步长是单个元素的步长。
&A
取数组名的地址就代表数组的地址,步长就是整个数组元素的步长。
*(&a) == a
一维数组的地址等于数组第一个元素的地址。
3、一个二维数组可以看成是由多个一维数组组成,单位是一维数组。
int aa[][];
啊
指针常量,保存一维数组的地址
&aa
二维数组的地址
*(&aa)=aa
二维数组的地址的值等于二维数组中第一个一维数组的地址。
*aa
二维数组中第一个一维数组的第一个元素的地址
4、一个三维数组可以看成是由多个二维数组组成的
int aaa[2][2][2];
1. 定义和初始化
2. 表达式说明:
啊啊
三维数组名,指针常量,保存三维数组中第一个二维数组的地址
*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));
}
}
}
}
5. 指针数组
字符*pa[3]
数组中存储的元素是指针
1. 注意:
a\Array 未初始化的指针是野指针。 它们的使用方式与指针相同。 使用时必须分配空间。 使用后,必须及时释放空间,留空。
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);
}
6. 数组指针
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 是一维字符数组的名称,是调用函数的名称
实参: (a) 形参:void (char * str)
(2)实参以二维数组名的形式传递,形参连接一维数组指针(相当于二维指针)
(aa) 字符 (*a)[]
(3)实参以三维数组名的形式传递,实参连接二维数组指针(相当于三维指针)
(aaa) 字符 (*a)[][]
补充:当数组作为形参时,编译器会将数组退化为指针,这样可以提高效率。
1、一维数组退化为一维数组元素类型指针(一维指针); 2、二维数组退化为指向一维数组的指针(二维指针); 3、三维数组退化为指向二维数组的指针指向元素的指针(3D指针)
指针数组和数组指针详解博客 - CSDN Array
三、指针和数组的区别 1、空间大小
数组是静态分配的空间(栈),并且空间是连续的。 定义就是分配空间,访问效率快。 存在一个问题,空间长度是固定的,不能改变。 很容易造成空间利用率低,造成空间浪费。 指针是动态分配的,并使用链表实现。 空间长度可以根据需要改变,需要用户手动分配空间(在堆上)。 使用完后,必须及时释放空间,防止内存泄漏。
2. 访问效率
数组可以快速连续访问,而指针则很灵活,可以用来随机访问数据。
3. 安全性
阵列有系统管理的空间,安全性好。 指针空间由用户管理,需要自己分配和释放,很容易造成内存泄漏,安全性较差。
4.函数参数(类型兼容性问题)
当数组作为形参时,编译器会将数组退化为指针,这样可以提高效率。
1、一维数组退化为一维数组元素类型指针(一维指针); 2、二维数组退化为指向一维数组的指针(二维指针); 3、三维数组退化为指向二维数组的指针指向元素的指针(3D指针)
4. 指针和数组的复杂声明(待补充)