今天,我们一起来C语言的下一个新知识点:数组。由于在之前,我们已经学习了C语言基本的数据类型(整型,浮点型,字符型等等),那么,如果我们想把一堆具有相同数据类型的数据放在一起,该如何实现呢?这就引出了数组的概念,数组就是具有相同数据类型的集合,下面先让我们学习一维数组。
1. 一维数组的创建和初始化
1.1 数组的创建
数组是一组相同数据类型的集合
例如,我们想创建五个整型变量,把他们放在一组,用C语言应该如何实现呢?
int arr[5]
如上图所示,int代表数组的元素类型,arr代表数组名,方括号里面的5代表这个数组中元素的个数,也就是这个数组的大小。
那么,有同学可能就要问了,那数组的大小只能是一个常量吗,能不能在方括号里面放一个变量,让他成为一个变长数组呢?
int arr1[10]; // √
int count = 0;
int arr2[count]; // 可以吗?
答案是:不一定
我们查阅有关C语言的资料可以得出,在C99标准之前,[ ]中必须要给一个常量,否则编译器会报错,而在C99标准中,引出了变长数组的概念,而在不同的编译器中,是否采取了C99标准是未知的(VS中会报错,而在Vscode中可以使用变长数组),所以避免bug的出现,我们最好还是给数组大小一个确定的值。
1.2 数组的初始化
我们在学习了数组的创建之后,因为我们要使用它,所以肯定要对他进行初始化
下面写出几个例子,通俗易懂
int arr1[10] = {1, 2, 3};
int arr2[] = {1, 2, 3, 4, 5};
int arr3[4] = {1, 2, 3, 4};
char arr4[3] = {'a', 'b', 'c'};
char arr5[] = "abcde";
上文给出了五个例子,有整型数组和字符型数组
1. 定义了一个数组大小为10的整型数组,前三个元素分别为1,2,3,由于未对剩余的七个元素初始化,所以默认他们为0,所以10个元素分别为1,2,3,0,0,0,0,0,0,0。(不完全初始化)
2. 定义了一个整型数组,长度没有给出,但是在数组中放进去了四个值,分别为1,2,3,4,注意:这时候数组的长度可以不传值,编译器会根据你后面放进去的值自动判断数组的大小,比如在这个例子中,数组的大小就是4
3. 定义了一个长度为5的整型数组,五个数分别为1,2,3,4,5。(完全初始化)
4. 同2,定义了一个长度未知的字符型数组,三个字符分别为'a', 'b', 'c'。这时候数组的长度为3
5. 因为字符数组也可以理解为一个字符串,所以字符数组也可以直接定义成字符串的形式,但是要注意的是,这时arr6中元素的个数为7,分别为'a', 'b', 'c', 'd', 'e', 'f', '\0',注意千万不要把'\0'给忘记了。
1.3 辨析 sizeof 和 strlen
下面进行一组对比
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = { 'a', 'b', 'c', 'd' };
char arr2[] = "abcd";
printf("sizeof(arr1) = %d\n", sizeof(arr1));
printf("sizeof(arr2) = %d\n", sizeof(arr2));
printf("strlen(arr1) = %d\n", strlen(arr1));
printf("strlen(arr2) = %d\n", strlen(arr2));
}
大家好像经常会对 sizeof和strlen搞混,下面我说一下sizeof和strlen的区别
1. sizeof是求整个数组的字节总数
2. strlen是求字符串的大小,遇'\0'停止
现在让我们来看一下编译结果
由于我们上文提到,arr2中还隐含了一个'\0',所以他的字节数比arr1多1
那么arr1的strlen为36又该如何解释呢?
其实,我们上文也提到,strlen是遇到'\0'停止,可是arr1中一共只有四个元素,没有'\0',所以对数组进行strlen求长度,结果是一个随机值。
相信大家对strlen和sizeof的区别已经很清楚了,那么让我们继续下面的学习吧。
1.4 一维数组的使用
刚才我们也介绍了这个操作符:[ ],他其实就是数组访问的操作符
比如,我们定义一个整型数组
#include <stdio.h>
int main()
{
int arr[10] = { 1, 2, 3 };
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++) printf("%d ", arr[i]);
}
我们可以通过sizeof来计算数组的大小,因为数组中每个元素的数据类型都是一致的,也就是每个元素的字节数都是一样的,用总字节数除以每个元素的字节数,就得到数组的长度
1.4 一维数组在内存中的存储
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 1, 2, 3 };
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++) printf("&arr[%d] = %p\n", i, &arr[i]);
}
下面让我们来看编译结果
我们不难看出,数组中每个元素的地址之间都相差四个字节,因为int整型本身就占四个字节,所以我们可以得出,随着数组下标的增长,元素的地址也在增加。
结论:数组在内存中是连续存放的。
2. 二维数组的创建和初始化(跟一位数组类似)
2.1 二维数组的创建
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr1[3][4];
char arr2[4][5];
double arr3[3][6];
}
2.2 二维数组的初始化
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr1[3][4] = { 1, 2, 3, 4 };
int arr2[3][4] = { {1, 2}, {3, 4} };
int arr3[][4] = { {2, 3}, {4, 5} }; // 行可以省略,列不能省略
}
2.3 二维数组的使用
二维数组的使用也是通过下标的方式
我们举例,来遍历二维数组的每一个元素
看代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr1[3][4] = { 1, 2, 3, 4 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
printf("%d ", a[i][j]);
}
printf("\n");
}
}
2.4 二维数组在内存中的存储
向一维数组一样,我们打印数组二维数组中每个元素的地址
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[3][4] = { 1, 2, 3, 4 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
printf("arr[%d][%d] = %p \n", i, j, &arr[i][j]);
}
}
}
我们还是可以看到,不管两个元素在不在同一列,他们之前的差都是四个字节,所以得出结论:二维数组在内存中也是连续存储的。
3. 数组作为函数的参数
我们下面来进行举例说明
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void Print(int arr[])
{
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++) printf("%d ", arr[i]);
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
Print(arr);
}
我们想让Print函数实现打印数组中所有元素的功能,于是我们在主函数中调用Print函数,我们期望的结果是1 2 3 4 5,可结果却是
有很多小伙伴到这里就蒙了,这是为什么呢?
这就是我接下来要讲的,有关函数传参的知识。
我们知道,数组名是数组首元素的地址(有关数组名的知识我们在下文会进行详细的讲解)
我们调用Print函数的时候,传进去的参数是arr,因为arr是数组名,是首元素的地址,所以形式参数应该用一个指针来代替,所以上面代码中的int arr[ ]实际上是一个指针变量,来存储数组首元素的地址,因为这是一个整型数组,所以sizeof(arr)自然为四个字节,而sizeof(arr[0])也为四个字节,所以实质上函数内部的数组大小sz = 1,所以最终编译的结果也就不难理解了。
那么,我们应该怎么做,才能避免这种现象的出现呢?
答:在函数传参的时候,把数组的长度也传递进去,也就是在主函数中提前计算数组的长度
例如:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void Print(int arr[], int size)
{
for (int i = 0; i < size; i++) printf("%d ", arr[i]);
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
int sz = sizeof(arr) / sizeof(arr[0]);
Print(arr, sz);
}
这样,我们再一次编辑代码,可以得到以下结果
这次,我们就成功地实现了,对数组的打印。
4. 对数组名的理解
大部分情况下,数组名就是数组首元素的地址,但是有两种情况例外
1.sizeof后面直接接数组名,这时候数组名代表整个数组
2.在使用&+数组名的时候,数组名也代表整个数组
其余时候,数组名都代表数组首元素的地址,希望大家能够记住这两种特殊情况,以免发生错误
好了,今天关于数组的分享到这里就结束了,大家对于数组的掌握有没有加深了呢?
下期,我们将运用我们之前学过的技术,来进行一个小小的游戏《三子棋》有关代码的讲解。
如果你喜欢我的博客,请留下你的评论和点赞,你们的支持就是对我最大的鼓励!
谢谢大家,我们下期再见,拜拜~