之前通过学习“变量”,我们知道变量就是一个容器,可以保存同类型的任意数据,并且可以修改。比如我想保存一下悟空的年龄:500,我就可以定义一个整型变量:age,这样内存中就会开辟出一个四个字节,叫做age的区域来保存“500”这个整型数,如果我突然想用age保存八戒的年龄:250,只需要将250赋给age就行了,500这个数值就会被250所覆盖。那么问题来了:学挖掘机哪家强?额。。。说正题,如果我想同时保存悟空和八戒的年龄怎么办呢?那我就需要定义出两个整形变量:age_Monkey和age_Pig,这样,用这两个变量就可以同时保存悟空和八戒的年龄信息了。再有一个问题:如果我想记录下整个取经之路上遇到的妖精、神仙的年龄,那可怎么办?我岂不是要定义出一大堆的整形变量了?好麻烦啊,有没有省事一点的办法?有!可以用数组!
一维数组
什么是数组?
在学习数组之前,我们先要思考一个问题:什么是数组?
数组和变量一样,都可以当做一个容器来保存一些数据,只不过数组一次性可以保存多个数据,而变量一次只能保存一条数据。
变量根据保存的数据类型不同可以分为:整型变量、浮点型变量和字符型变量,整形变量里只能保存整型数,字符型变量里只能保存字符(注意:字符是单个的符号,用' '
括起来,后便我们会学习字符串,注意和字符串区分开),浮点型变量里只能保存小数。
数组和变量类似,根据数组中保存的数据类型可以分为:整型数组、浮点型数组、字符型数组。需要注意的是,整形数组中可以保存不止一条数据,但是这么多数据的类型必须是整型的,浮点型数组也是,它里边存放的数据必须是浮点型,不能是其他数据类型,字符型数组同理。换句话说:某一类型的数组,只能保存对应类型的数据,这一点和我们已经学过的变量时非常相似的。
如何定义一个数组?
知道了什么是数组(能够保存若干条相同类型数据的一种容器),我们就要学习如何定义出这个比变量还要强大的东西。
数组的定义和变量的定义类似。我们定义一个变量,需要先确定变量的类型(用类型修饰符)、然后给变量去名字(变量名),最后给变量赋初值。代码如下:
// 定义一个整型变量
int a = 0; // int是类型修饰符,a是变量名,0是初值
定义一个数组也需要先确定数组的类型(使用类型修饰符),数组的类型一确定,那么这个数组里保存的数据必须是同类型的数据;确定了数组的类型后,就需要给数组取一个名字了(数组名)。在定义基本数据类型的变量时,到这一步就可以结束了,但是定义数组时,还需要指定数组的长度。
长度?什么是“数组的长度”?我们知道数组可以保存若干条相同类型的数据。那么“若干条”到底是几条呢?这就是由数组长度确定的。一个数组最多可以保存多少条数据,完全取决于定义数组时指定这个数组的长度。
指定完数组长度后,就可以给数组赋值了。我们看一下定义数组的代码:
// 定义一个整形数组
int array[5] = {12, 23, 34, 45, 56};
通过上边的代码,我们就定义出了一个数组,我们来解析一下代码:
-
int
:int是数据类型修饰符,和定义一个变量一样,我们定义数组时也需要声明一下数组的类型,这样这个数组能保存那种类型的数据就可以确定了。 -
array
:这个array就是数组名,和变量名一样,数组名也可以使用字母、数字、下划线的任意组合,但是数字不能做开头.
3.[5]:这个东西就是指定数组的长度,在这里,我们指定array
这个数组的长度是5([]中只能是常量表达式,不能使用变量(C99标准之后可以用变量,但是如果使用变量的话不能给初值))。那么系统就会在内存中给我们分配五个单位的字节数。那一个单位的字节数是几个字节呢?这取决于数组的类型,数组是什么类型的,那么一个单位的字节数就是对应类型所占用的字节数。这里的数组是整型数组,一个整型占用4个字节,所以“五个单位的字节数”就是4*5=20个字节,也就是说,这个数组可以存放五个整型数,这个数组在内存中占据20个字节的内存空间。 -
{12, 23, 34, 45, 56}
:这就是给array
这个数组赋值。给数组赋值,需要用{}
将值括起来,数值与数值之间使用,
分隔,数组的值被称为数组元素。
以上是定义一个数组的完整形式,我们也可以像下边这样定义一个数组出来:
int array1[5] = {1, 2, 3}; // 数组有五个元素,赋值时只给了三个元素,默认后边两个元素为0
int array2[5] = {0}; // 数组有五个元素,赋值时什么都没有,默认五个元素都是0
int array3[] = {1, 3, 5, 7, 9}; // 没有指定数组的元素个数,系统会根据赋初值时元素的个数推断数组的长度
如何使用数组?
访问、修改数组元素
我们把数据存放在数组中的目的是在未来的某个时刻使用这些数据,那么如何访问这些数据呢?访问数组的元素,我们可以使用数组名+下标的方式访问数组的元素。
在计算机中,计数是从零开始的,所以第一个元素的下标是“0”,依次类推。当我们要访问数组array
的第1个元素时,使用数组名+下标的方式来访问,就是如下的写法:
int array[5] = {12, 23, 34, 45, 56};
int num = array[0]; // 取出数组array的第一个元素,赋给整形变量num
如果我们打印num
的值,就是12
,数组的第一个元素(下标为0)的值。
注意:数组名+元素下标的写法和定义一个数组时的数组名+数组长度写法非常相似。我们要准确区分方框中的数字表示的含义:当方框出现在定义数组时,表示声明数组的长度(也就是数组名前有类型修饰符时,方框中的数字表示:声明数组的长度),当处在在访问数组元素中是,方框表示要访问的那个元素的下标(也就是数组名前没有类型修饰符时,方框中的数字表示:元素的下标或位置)。
上边是访问(获取)数组某个元素的值,如果我要修改数组元素的值怎么办?很好办,先访问到这个元素,在使用赋值运算符=
来给这个位置上的元素赋新值。
// 定义一个整形数组
int array[5] = {12, 23, 34, 45, 56};
// 修改下标为3的元素的值,改为55
array[3] = 55; // 将数字55赋给array数组中下标为3的元素
// 打印下标为3的元素的值
printf("array[3] = %d", array[3]);
第二行代码就是修改数组元素的值的代码,修改完成后再打印下标为3的元素,结果就是55
。
遍历数组
像上边那样,我们可以使用数组名+元素下标的方式来访问数组元素,但是,如果需要访问整个数组的元素呢?比如我们需要把数组array1
的值,赋给数组array2
,我们要怎么办?可以像基本数据类型的变量那样直接赋值吗?
array2 = array1
。这样写看起来似乎没什么问题,但是数组不能整体赋值,如果要把array1的元素赋给array2,需要逐一获取array1各个元素的值,再逐一复制给array2对应的下标位置。就像这样:
int array1[5] = {1, 2, 3}; // 数组有五个元素,赋值时只给了三个元素,默认后边两个元素为0
int array2[5] = {0}; // 数组有五个元素,赋值时什么都没有,默认五个元素都是0
array2[0] = array1[0];
array2[1] = array1[1];
array2[2] = array1[2];
array2[3] = array1[3];
array2[4] = array1[4];
但是,这样写看起来好麻烦啊,入股数组有5个元素的话,工作量还能接受,如果数组有500个元素可怎么办?这时候,我们就可以利用上节课学的for
循环来逐一访问数组元素了,代码如下:
int array1[5] = {1, 2, 3, 4, 5}; // 数组有五个元素,赋值时只给了三个元素,默认后边两个元素为0
int array2[5] = {0}; // 数组有五个元素,赋值时什么都没有,默认五个元素都是0
// 使用for循环遍历数组
for (int i = 0; i < 5; i++) { // i从0开始,因为i将作为数组的下标使用;i<5,因为数组元素最大值是“数组元素个数-1”
int temp = array1[i]; // 可以使用变量作为数组的下标
printf("temp = %d", temp);
}
我们可以使用for循环遍历数组的每一个元素,既然能遍历到数组的每一个元素,就可以给array2赋值了,代码如下:
int array1[5] = {1, 2, 3, 4, 5}; // 数组有五个元素,赋值时只给了三个元素,默认后边两个元素为0
int array2[5] = {0}; // 数组有五个元素,赋值时什么都没有,默认五个元素都是0
// 使用for循环遍历数组
for (int i = 0; i < 5; i++) { // i从0开始,因为i将作为数组的下标使用;i<5,因为数组元素最大值是“数组元素个数-1”
array2[i] = array1[i]; // 可以使用变量作为数组的下标
printf("array[%d] = %d", i, array2[i]);
}
注意事项
- C语言中,并没有“数组下标越界保护”,所以如果数组的下标不小心超过了数组的长度,编译器并不会报错,程序也能正常执行,但是,获取到的数据不是我们想要的数据,最终会导致程序出现bug。所以我们在访问数组元素时,千万不要让数组下标越界。
- 数组是一个整体,不能直接参加运算,只能对单个元素进行处理,通常用到数组的地方都会用到循环。
数组排序(冒泡排序)
冒泡排序的原理
冒泡排序的流程
一共5个数字:9 7 5 3 2
第一趟:
1、9和7比较,9比7大,进行交换 变成了7 9 5 3 2
2、然后9和5比较,9比5大,进行交换,变成了7 5 9 3 2
3、9和3比较,9比3大,进行交换,变成了7 5 3 9 2
4、9和2比较,9比2大,进行交换,变成了7 5 3 2 9。
这样第一趟是不是已经结束了,并且最大的数已经移动到了最后一位。第一趟,比较了几次?4次
第二趟:
1、7和5比较,7比5大,进行交换,变成了5 7 3 2 9
2、7和3比较,7比3大,进行交换,变成了5 3 7 2 9
3、7和2比较,7比2大,进行交换,变成了5 3 2 7 9。
这样第二趟结束了,比较了3次
第三趟:
1、5和3比较,5比3大,进行交换,变成了3 5 2 7 9
2、5和2比较,5比2大,进行交换,变成了3 2 5 7 9。
这样第二趟结束了,比较了2次
第四趟:
1、3和2比较,3比2大,进行交换,变成了2 3 5 7 9.
这趟结束,比较了1次
比较了4(5 - 1)趟,第一趟比较了4次,第二趟比较了3次,第三趟比较了2次,第四趟比较了1次。
总结规律
使用冒泡法给数组排序,比较的趟数是数组元素的个数-1趟。每趟比较的次数是数组元素的个数 - 当前趟数。假设数组有 n 个元素,则需要比较 n-1 趟,每趟比较 n-趟数 次
C语言实现冒泡排序
在搞清楚了冒泡排序算法的规则之后,我们通过代码来实现冒泡排序。
// 定义一个整形数组
int array[5] = {34, 25, 46, 45, 12};
// 使用冒泡对数组进行排序
// 两层for循环,外层控制比较的趟数(5-1趟),内层比较每趟比较的次数(元素个数-当前趟数)
for (int i = 0; i < 5-1; i++) { // 比较的趟数是元素个数减1
for (int j = 0; j < 5 - (i + 1); j++) { // 由于趟数是从0开始,所以在元素个数减去趟数前,要先将趟数加一
if (array[j] > array[j+1]) { // 判断如果前一个元素大于后一个元素,则交换两个元素的值
int temp = array[j]; // 将前一个元素的值先取出来放在临时变量中
array[j] = array[j+1]; // 将后一个元素的值交给前一个元素
array[j+1] = temp; // 将临时变量的值(也就是前一个元素的值)交给后一个元素
}
}
}
// 冒泡法排序完成,遍历打印数组
for (int i = 0; i < 5; i++) {
printf("%d ", array[i]);
}
字符数组
1、用于存放字符变量的数组,称为字符数组,属于一维数组。
2、元素类型是char类型。字符类型
3、字符数组的定义可以使用3、字符数组可以采用{‘i’, ‘P’, ‘h’, ‘o’, ’n’, ‘e’}或“iPhone”,这两个有什么区别呢?后面的也称为字符串,C语言是没有这种类型的。
4、有’\0’结束标识的字符数组也称为字符串
5、字符数组的常用函数
strlen()计算字符串的长度,以\0结尾,但是计算的长度不包括\0
strcpy()字符串拷贝,strcpy(目标字符串, 源字符串),PS:目标字符串的长度大于源字符串的长度???
strcat()字符串拼接,strcat(字符串1, 字符串2),拼接的结果存放在字符串1中
strcmp()字符串比较,比较两个字符串(str1, str2)的大小,如果str1 == str2,返回零,str1 > str2,返回正数,str1 < str2,返回负数。
6、字符串占用的空间要比我们看到的字符串的长度大1,因为结束的时候有\0,\0是字符串的结束符
7、字符数组,尽量以字符串形式赋值,避免我们忘记添加’\0’
网友评论