C语言——详解数组
一、数组的概念
1.1 引言
学习C语言的过程中,随着学习的深入,我们有时会面临着处理多个数据的情况,用原来的知识处理难免有些繁琐。这时,我们便需要掌握一项新的技能——数组。今天,就由我来给大家介绍这个重要的结构。
1.2 数组是什么
数组是一种在编程语言中用于存储和组织多个相同类型的数据元素的数据结构。它是一个连续的内存块,其中每个元素都具有唯一的索引。这使得程序可以快速访问数组中的任何元素。
大家可能看的一脸懵,简单点说:
数组就是一组相同类型元素的集合
从这句话中,我们不难看出数组满足两个关键点:
- 数组是一种集合,因此存放的是一个或多个数据。
但是数组元素个数不能为0
- 数组中存放的多个数据,
类型必须是相同的
数组分为一维数组和多维数组,其中多维数组最常见的是二维数组。
二、一维数组
2.1 数组的创建与初始化
(1)数组的创建
一维数组创建的基本语法如下:
type arr_name[常量值];
- 存放在数组的值被称为数组的元素,数组在创建的时候可以指定数组的大小和数组的元素类型
1,type指定的是数组中存放的数据类型,可以是:char、short、int、float等,当然,也可以是自定义类型
2, arr_name指的是数组的名字,这个名字自己取,但最好要取的有意义
3,【】中的常量值是用来指定数组的大小,这个数组的大小根据实际的需求来制定就行
例如: 想存储班上50人的总分成绩,我们创建如下数组
int grades[50];
再如:
float arr1[20];
double arr2[30];
char arr3[10];
注:创建数组是【】中必须给一个常量,不能是变量。(C99之后,支持变长数组,稍后介绍)
(2)数组的初始化
正如我们创建变量时,往往需要将变量初始化,创建数组是也是如此,下面,就让我们看看数组是如何初始化的吧。
数组的初始化往往需要用到大括号,将数据存放在大括号中。
int arr1[5] = { 1, 2, 3, 4, 5 };
//完全初始化
int arr2[5] = { 1 };
//不完全初始化
如果是 int float 等类型的不完全初始化,其余值默认为0。
当不指定数组的大小时,初始化时有几个元素,数组就是多大,让我们用sizeof1关键字来看看下面数组大小
int main()
{
int arr[] = { 1, 2, 3, 4 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d ", sz);
return 0;
}
运行结果:
下面是错误的初始化,程序报错
初始化元素个数要小于或等于数组大小
int arr[3] = { 1, 2, 3, 4 };
字符数组的初始化
下面让我们看看以下两种不同的字符数组初始化
运行结果:
我们可以看到,arr1字符数组在打印时,除了a、b、c、d后还没结束,还打印了一些随机值,而arr2的打印是完全正常的,这是为什么呢。
原来,字符串默认是有‘\0’作为结束标志,在第一种初始化方法中,输入的字符串后面是有一个不显示的‘\0’,数组知道什么时候结束,要取多大.
而第二种在末尾的地方没有‘\0’字符作为结束标志,数组不知道什么时候停止。因此,这是一种错误的初始化方式。
以下是arr2数组正确的初始化方式:
char arr2[] = { 'a','b','c','d','\0' };
(3)数组的类型
数组也是有类型的,数组的类型算是一种自定义类型,去掉数组名留下的就是数组类型
。
如下:
int arr1[10];
int arr2[8];
char ch[5];
arr1数组的类型是int [10]
arr2数组的类型是int [8]
ch数组的类型是char [5]
注意:int、char是数组元素的类型,而不是数组的类型
2.2 数组的使用
(1)数组的下标
我们了解了一维数组的基本语法,那么我们该如何使用一维数组呢?
C语言规定数组是有下标的,下标从0开始的,假设数组有 n n n个元素,那么最后一个元素的下标就是 n − 1 n-1 n−1。
例如:
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
数组
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
10
|
下标
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
C语言中数组访问提供了一个操作符[ ]
,这个操作符叫:下标引用操作符
当我们想访问下标为7的元素时,可以使用arr[7],想访问下标为3的元素,就可以使用arr[3]。
如下:
#include<stdio.h>
int main()
{
int arr[10] = { 1, 2, 3, 4 , 5 , 6, 7, 8, 9, 10 };
printf("%d ", arr[7]);
printf("%d ", arr[3]);
return 0;
}
(2)数组的输入与输出
数组的输出与变量的输入差不多,只要我们产生数组的相应下标就可以啦,这时我们就需要使用到for循环。
#include<stdio.h>
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;
}
输入也是同理
#include<stdio.h>
int main()
{
int arr[10];
int i = 0;
for (i = 0; i < 10; i++)
{
scanf("%d", arr[i]);//循环向数组中输入元素
}
return 0;
}
输出结果:
2.3 数组在内存中的存储
前面我们知道了数组的基本知识,但要想更深入了解数组,那么了解它在内存中的存储必不可少。
依次打印数组的地址和数组元素的地址:
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
printf(" &arr = %p\n", &arr);//打印数组的地址
printf("\n");
for (i = 0; i < 10; i++)
{
printf("&arr[%d] = %p\n", i, &arr[i]);//打印数组中各个元素的地址
}
return 0;
}
输出结果:
我们可以看到,随着数组下标的增长,地址是从小到大变化的,且相邻两个下标之间相差 4(int类型是4个字节)。我们可以得出结论:数组在内存中是连续存放的
。同时,我们还发现数组的地址和数组首元素地址相同
,即&arr == &arr[0]
.
三、二维数组
3.1 数组的创建与初始化
什么是二维数组呢,当我们把一维数组作为数组的元素,就是二维数组。
(1) 数组的创建
二维数组创建的语法如下:
type arr_name[常量值1][常量值2]
例如:
int arr[3][5];
double data[6][7];
二维数组的创建与一维数组类似
其中常量值1表示行,常量值2表示列。
(1) 数组的初始化
不完全初始化
int arr1[3][3] = {1, 2};
示意图:
1
|
2
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
完全初始化
int arr2[3][3] = {1,2,3, 4,5,6, 7,8,9};
示意图:
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
按行初始化
int arr3[3][3] = {{1,2},{3,4}};
示意图:
1
|
2
|
0
|
3
|
4
|
0
|
0
|
0
|
0
|
二维数组可以省略行,
但不能省略列
当二维数组的第一行按指定格式被填满时,再从第二行开始填,填到几行数组就是几行
例如:
int arr4[][3] = {1,2,3,4,5,6,7};
示意图:
1
|
2
|
3
|
4
|
5
|
6
|
7
|
0
|
0
|
例如:
int arr5[][3] = {{1,2}. {1.2}, {1,2}};
示意图:
1
|
2
|
0
|
1
|
2
|
0
|
1
|
2
|
0
|
3.2 数组的使用
(1)数组的下标
二维数组与一维数组一样,访问也是使用下标形式,不同的是,二维数组有行和列,需要有两个下标
C语言中规定,二维数组的行是从0开始,列也是从0开始
例如:
int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
示意图:
代码演示:
#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[1][3]);
printf("%d\n", arr[0][2]);
return 0;
}
运行结果:
(2)数组的输入与输出
与一维数组类似,想访问整个二维数组需要用到循环遍历所有下标,不同的是,二维数组需要用到循环的嵌套。
代码如下:
#include<stdio.h>
int main()
{
int arr[3][5];
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.3 数组在内存中的存储
像一维数组一样,如果想研究二维数组在内存中的存储方式,我们也是可以打印出数组所有元素的地址的。代码如下:
#include<stdio.h>
int main()
{
int arr[3][5] = { 0 };
int i = 0;
int j = 0;
printf(" &arr = %p\n", &arr);
printf("\n");
for (i = 0; i < 3; i++)
{
printf(" &arr[%d] = %p\n", i, &arr[i]);
}
printf("\n");
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;
}
运行结果:
从输出结果来看,不难看出:
1、 二维数组内,每一行内部的每个元素都是相邻的,地址之间相差4字节,跨行位置处的两个元素(如:arr[ 0 ][ 4 ]和arr[ 1 ][ 0 ])之间也是差4字节。所以二维数组中的每个元素都是连续存放的。
2、 数组的地址与数组首元素地址相同,数组每行地址与每行首元素地址相同
示意图:
四、变长数组
在C99标准之前,C语⾔在创建数组的时候,数组⼤⼩的指定只能使⽤常量、常量表达式,或者如果我们初始化数据的话,可以省略数组⼤⼩。
例如:
int arr1[10];
int arr2[3 + 5];
int arr3[]; = { 1, 2, 3 };
但是这样的语法限制,让我们创建数组就不够灵活,有时候数组⼤了浪费空间,有时候数组⼜⼩了不够⽤,所以在C99中给⼀个变长数组(variable-length array,简称VLA)的新特性,允许我们可以使⽤变量指定数组⼤⼩。
代码如下:
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int arr[n] = { 0 };
return 0;
}
上面示例中,数组arr就是变长数组,因为它的长度取决于n的值,编译器无法事先确定,只有运行时才能知道n是多少。
同时,因为变长数组的长度只有运行时才能确定,所以变长数组的长度不能初始化
。
注:变长数组一旦创建,大小就无法改变
(改变变量也不行)
好啦,本期关于数组就介绍到这里啦,希望本期博客能对你有所帮助,同时,如果有错误的地方请多多指正,让我们在C语言的学习路上一起进步!
sizeof的解释
sizeof是C语言中的一个关键字,可以计算类型或者变量所占用字节的大小,也可以用来计算数组的大小。
我们知道数组所以元素的类型都是相同的,这时,我们可以用数组的总大小除以单个元素所占用的大小,就可以求出数组的元素个数了。 ↩︎