希望文章能对你有所帮助,有不足的地方请在评论区留言指正,一起交流学习!
目录
1. 数组的概念
数组是⼀组相同类型元素的集合;
• 数组中存放的是1个或者多个同类型的数据,但是数组元素个数不能为0。
• 数组中存放的多个数据,类型是相同的。
数组分为⼀维数组和多维数组,多维数组⼀般⽐较多⻅的是⼆维数组。
2.一维数组
2.1 ⼀维数组的创建
type_t arr_name [const_n]
type_t 指定数组中存放数据的类型,可以是: char、short、int、float 等,也可以自定义的类型。
arr_name 数组的名字
const_n 一个常量表达式,用来指定数组的大小
数组创建实例
//代码1
int arr1[5 + 6];//常量表达式
int arr2[5];
char ch[8];
double score[10];
//代码2
//使用变量来指定数组大小
int n = 10;
int arr3[n];//数组可以正常创建吗?
代码2 在VS2017的编译情况下,会出现报错。因此在VS2017的环境下,创建变量要使用常量。
2.2.数组初始化
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。
数组的初始化⼀般使⽤⼤括号,将数据放在大括号中。
整型数组在不初始化的条件下,每个元素都默认为0
初始化程序
整型数组
(1)完全初始化 数组大小等于元素个数,如下
int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };
(2)不完全初始化 数组大小小于元素
int arr2[10] = { 0,1,2,3,4 };
剩余元素默认为0,可以调出监视窗口查看,发现从第五个元素之后的值都为0。
(3)不指定数组大小
int arr4[] = { 1,2,3 };
数组通过确定初始化的元素个数确定数组的大小
字符数组初始化
//代码1
char arr6[3] = { 'a','b','c' };
char arr7[] = { 'a','b','c' }; //两种是一样的
//代码2
char arr8[10] = "abc";//使用字符串初始化 C后面还有一个"abc\0" 字符串中自带一个 \0
char arr9[] = "abc"; //有四种元素 自带"\0" 看调试
//代码3
char arr10[3]="abc";
代码1创建的字符数组中的变量是相同的 代码3和代码1中的初始化是一样的
代码2利用字符串创建的变量中,前四个元素值相同,数组的大小不同。使用字符初始化的话附赠一个'\0',字符串结束的标志。没有初始化的元素,默认为'\0'。
代码1和代码2的区别利用如下代码:
int main()
{
char arr1[] = "abc";// 四个元素
char arr2[] = { 'a','b','c' };//两者在内存中存储的区别,就决定了使用效果的区别 三个元素
printf("%s\n",arr1); //abc
printf("%s", arr2); //abc烫烫烫烫蘟bc 打印出来是随机值 本质就是内存的存储
//存储的是随机值 arr1中有\0
//内存是连续的
return 0;
}
运行结果如下
上述数组均为限制数组的大小,需要有结束标志结束数组。在arr2中的初始化中没有'\0',程序就会去寻找'\0'这个结束标志,而内存中存储的是随机值,因此出来的也是随机值。
2.3⼀维数组的使用
⼀维数组可以存放数据,存放数据的目的是对数据的操作,如何调取数组?
(1)数组的下标
数组是有下标的,下标是从0开始的,假设数组有n个元素,最后⼀个元素的下标是n-1,下标就相当于数组元素的编号,如下:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
[ ] ,这个操作符是下标引⽤操作符。通过下标引操作符就可访问数组中的每一个元素了。
要访问第七个元素可以使用 arr[7] ,想要访问下标是3的元素,就可以使用 arr[3]。
代码
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", arr[7]);//8
printf("%d\n", arr[3]);//4
return 0;
}
如何输出整个数组的内容呢?
使⽤for循环产生0~9的下标,接下来使用下标访问就行了。目前看来数组的输出只能通过循环实现。
如下代码:
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
如何输入呢?
利用for循环
int main()
{
int arr[10] = { 0 };
int i = 0;
for (i = 0; i < 10; i++)
{
scanf("%d", &arr[i]);
}
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
2.4一维数组在内存中的存储
我们直接用程序来看每一个元素的地址,代码如下
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
for (i = 0; i < 10; i++)
{
printf("&arr[%d] = %p\n", i, &arr[i]);
}
return 0;
}
运行的结果
可以发现
1.每个元素相差四个字节(一个普通整型的大小),
2.一维数组在内存中是连续的存放的,随着数组下标的增长,地址是由低到高变化的,值小的叫做低地址 ,大的叫做高地址。
图解
2.5 sizeof计算数组元素个数
使用sizeof ,是可以计算类型或者变量⼤⼩的,其实 sizeof 也可以计算数组的大小。
sizeof()所求得的结果单位是字节。
需要注意的是sizeof(arr)中的arr所代表的整个整型数组,arr[0]可以代表一个 一个元素。
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr)/sizeof (arr[0]));
return 0;
}
运行结果如下
计算得出整个数组有10个元素的整型数组,多占空间是40个字节
3.二维数组
如果我们把⼀维数组做为数组的元素,这时候就是⼆维数组,⼆维数组作为数组元素的数组被称为三维数组,⼆维数组以上的数组统称为多维数组。
3.1 ⼆维数组的创建
type arr_name[常量值1][常量值2];
int arr[3][5];
double data[2][8];
二维数组的创建行可以省略但是列一定不可以省略,如果列省略后,就无法确定数组有几行了。
3.2⼆维数组的初始化
int arr1[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};//完全初始化三行四列
int arr2[3][4] = { 1,2,3,4,5 };//不完全初始化 没有赋值的默认是0
int arr1[3][4] = { {1,2},{3,4},{5,6} };//按照什么方式初始化 一维数组
//行是可以省略的,列不可以
int arr1[3][4] = { 1,2,3,4,5,6,7,8,9 };
int arr1[][4] = { 1,2,3,4,5,6,7,8,9 };//二者相同
二维数组完全初始化和不完全初始化和一位数组类似。
(1)完全初始化
int arr1[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
(2)不完全初始化
int arr2[3][4] = { 1,2,3,4,5 };//不完全初始化 没有赋值的默认是0
(3)按照行初始化
int arr1[3][4] = { {1,2},{3,4},{5,6} };
说明,在{ }中使用的{ }表示是给数组其中的一行所赋的值。
3.3⼆维数组的使用
⼆维数组的行是从0开始的,列也是从0开始的
int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
(1)数组的访问
按照列号和行号访问指定元素,例如 11是第四列,第三行。
#include <stdio.h>
int main()
{
int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
printf("%d\n", arr[2][4]);
return 0;
}
输入和输出
类似于一维数组,不过我们需要的是两个变量,采用的for循环的嵌套。
int main()
{
int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
int i = 0;
//输⼊
for (i = 0; i < 3; i++) //产⽣⾏号
{
int j = 0;
for (j = 0; j < 5; j++) //产⽣列号
{
scanf("%d", &arr[i][j]); //输⼊数据
}
}
//输出
for (i = 0; i < 3; i++) //产⽣⾏号
{
int j = 0;
for (j = 0; j < 5; j++) //产⽣列号
{
printf("%d ", arr[i][j]); //输出数据
}
printf("\n");
}
return 0;
}
运行结果如下
3.4 ⼆维数组在内存中的存储
像⼀维数组⼀样,我们如果想研究⼆维数组在内存中的存储⽅式,我们也是可以打印出数组所有元素的地址的。代码如下:
int main()
{
int arr[3][5] = { 0 };
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
}
}
return 0;
}
运行结果
可以看出二维数组之间也是连续的每个元素的起始地址相差4个字节,并且二维数组在内存中也是连续存在的。下图会帮助你更好的理解
如图可以明确的二维数组在内存中的方式和一维数组是一样的,因此我们可以将二维数组看做成一维数组的数组
4.数组应用
4.1数组名字应用
数组名字是首元素的地址
但是有两个例外
1.sizeof(数组名字),这里的数组名表示的是整个数组,计算的整个数组的大小,单位是字节
2.&数组名字,在这里表示整个数组,&数组名取出的是数组的地址
查看实例
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
//代码1
printf("%p\n",arr);
printf("%p\n", &arr[0]);
//代码2
printf("%p\n", &arr);
return 0;
}
运行结果如下
三者的结果相同,但是代码1两种的表示方式一样,和代码2表示的不一样如下面的程序
举出例子证明一下
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
//代码1
printf("%p\n",arr);
printf("%p\n", arr+1);
//代码2
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0]+1);
//代码3
printf("%p\n", &arr);
printf("%p\n", &arr+1);
return 0;
}
运行结果如下
总结
函数名字就是函数的首个元素的地址和去首个元素地址相同,但是和取数组地址是不相同的。理解好整个有助于下面数组指针的学习。二维数组类似。
指针+1到底跳过几个字节和指针的类型有关。
4.2数组作为函数参数
写代码的时候,会将数组作为参数,比如:实现冒泡排序函数
什么是冒泡排序
思想:两两相邻元素进行比较,有可能的话需要交换,目标是实现一组数字的升序。
程序如下
void Sort(int arr[], int sz)
{
//趟数
int i = 0;
for ( i = 0; i < sz - 1; i++)
{
//一趟冒泡排序,决定一趟排序进行多少比较
int j = 0;
for ( j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main()
{
//整型数据
int arr[] = { 9,6,8,5,2,3,1,4,7,0};
int sz = sizeof(arr) / sizeof(arr[0]);
Sort(arr, sz);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
解释
程序执行过程中传递过去的是数组,因此接受的也是形式数组。
但是本质上,传递过去的是数组的首个元素的地址,因为数组在内存中的排序是连续的,因此可以沿着顺序找到各个元素。函数可以访问同一份数据,效率比较高所以子函数的程序也可以写成
void Sort(int* arr, int sz)
为什么传递的是地址呢
数组传递 假设值传递形式(10000个元素),拷贝数据浪费时间和空间 ;仅仅传递是地址效率很高,同时访问同一组数据。
再举一个例子加深理解
void Sort(int arr[], int*pa)
{
printf("%d ", arr[1]);
printf("%d ", *(pa+1));
}
int main()
{
int arr[] = { 9,6,8,5,2,3,1,4,7,0 };
Sort(arr, arr);
return 0;
}
输出的结果
两者的输出结果是相同,在指针的基础上+1就是下一个元素,和arr[1]的值相同,证明了本质上传递的是数组的首个元素的地址,是指针。*(arr+i)与arr[i]相同,&arr[i]和arr+i相同。
5.数组越界
数组的访问是有边界的,比如数组中有10个元素,所以下标是0~9的范围,这就是边界。数组下标的范围就是数组的边界。所以数组的下标小于0,或者大于n-1,就是数组越界访问了。
但是越界了并不代标着编译器报错,这个时候可能不会报错,所以我们自己要做好越界检查。如下图,数组访问的已经越界了,程序没有警告,依然出现了随机值