数组的基本概念
本小节知识点:
- 【理解】数组的基本概念
- 【理解】数组的分类
1.数组的基本概念
-
数组,从字面上看,就是一组数据的意思,没错,数组就是用来存储一组数据的
-
在C语言中,数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。
- 注意:只能存放一种类型的数据
-
数组的几个名词
- 数组:一组具有相同数据类型的数据的有序的集合
- 数组元素:构成数组的数据。数组中的每一个数组元素具有相同的名称,不同的下标,可以作 为单个变量使用,所以也称为下标变量。
- 数组的下标:是数组元素的位置的一个索引或指示。(从0开始)
- 数组的维数:数组元素下标的个数。根据数组的维数可以将数组分为一维、二维、三维、多维 数组。
-
数组的应用场景
- 一个int类型的变量能保存一个人的年龄,如果想保存整个班的年龄呢?
- 第一种方法是定义很多个int类型的变量来存储
- 第二种方法是只需要定义一个int类型的数组来存储
- 一个int类型的变量能保存一个人的年龄,如果想保存整个班的年龄呢?
2.数组的分类
-
按存储的内容分类
- 数值数组:用来存储数值得
- 字符数组:用来存储字符 ‘a’
- 指针数组:用来存放指针(地址)的
- 结构数组:用来存放一个结构体类型的数据
- ... ...
-
按维度分类
- 一维数组
- 二维数组
- 多维数组
数组的定义、初始化、使用
本小节知识点:
- 【掌握】定义数组
- 【掌握】初始化数组
- 【掌握】数组的使用
1.定义数组
- 元素类型 数组名[元素个数];
- 示例:
int ages[10];
2.初始化数组
-
一般会在数组定义的同时进行初始化
- 其中在{ }中的各数据值即为各元素的初值,各值之间用逗号间隔
int ages[3] = {4, 6, 9};
- 其中在{ }中的各数据值即为各元素的初值,各值之间用逗号间隔
-
指定数组的元素个数,对数组进行部分显式初始化
- 定义的同时对数组进行初始化,没有显式初始化的元素,那么系统会自动将其初始化为0
int nums[10] = {1,2};
- 定义的同时对数组进行初始化,没有显式初始化的元素,那么系统会自动将其初始化为0
-
不指定元素个数,定义的同时初始化,它是根据大括号中的元素的个数来确定数组的元素 个数
int nums[] = {1,2,3,5,6};
-
指定元素个数,同时给指定元素进行初始化
int nums[5] = {[4] = 3,[1] = 2};
-
先定义,后初始化
int nums[3]; nums[0] = 1; nums[1] = 2; nums[2] = 3;
-
没有初始化会怎样?
- 如果定义数组后,没有初始化,数 组中是有值的,是随机的垃圾数,所以如 果想要正确使用数组应该要进行初始化。
int nums[5]; printf("%d\n", nums[0]); printf("%d\n", nums[1]); printf("%d\n", nums[2]); printf("%d\n", nums[3]); printf("%d\n", nums[4]); 输出结果: 0 0 1606416312 0 1606416414
- 注意:对于数组来说,一旦有元素被初始 化,其他元素都被赋值0
- 如果定义数组后,没有初始化,数 组中是有值的,是随机的垃圾数,所以如 果想要正确使用数组应该要进行初始化。
3.数组的使用
- 通过下标(索引)访问:
ages[0]=10; int a = ages[2]; printf("a = %d", a);
数组注意事项
本小节知识点:
- 【掌握】数组注意事项
1.数组注意事项
-
在定义数组的时候[]里面只能写整型常量或者是返回整型常量的表达式
int ages4['A'] = {19, 22, 33}; printf("ages4[0] = %d\n", ages4[0]); int ages5[5 + 5] = {19, 22, 33}; printf("ages5[0] = %d\n", ages5[0]); int ages5['A' + 5] = {19, 22, 33}; printf("ages5[0] = %d\n", ages5[0]);
-
错误写法
// 没有指定元素个数,错误
int a[];
[]中不能放变量
int number = 10;
int ages7[number]; // 不报错, 但是没有初始化, 里面是随机值
printf("%d\n", ages7[4]);
int number = 10;
int ages7[number] = {19, 22, 33} // 直接报错
int ages8[5];
// 只能在定义数组的时候进行一次性(全部赋值)的初始化
int ages10[5];
ages10 = {19, 22, 33};
// 一个长度为n的数组,最大下标为n-1, 下标范围:0~n-1
int ages11[4] = {19, 22, 33}
ages[8]; // 数组角标越界
数组遍历
本小节知识点:
- 【掌握】数组的遍历
- 【掌握】数组长度计算方法
- 【掌握】练习
1.数组的遍历
-
数组的遍历:遍历的意思就是有序地查看数组的每一个元素
-
示例 ``` int ages[4] = {19, 22, 33, 13}; for (int i = 0; i < 4; i++) {
printf("ages[%d] = %d\n", i, ages[i]);
}
##2.数组长度计算方法
- 因为数组在内存中占用的字节数取决于其存储的数据类型和数据的个数
数组在内存中占用的总字节数:sizeof(数组名);
- 数组所占用存储空间 = 一个元素所占用存储空间 * 元素个数(数组长度)
- 所以计算数组长度可以使用如下方法
数组的长度 = 数组占用的总字节数 / 数组元素占用的字节数
int ages[4] = {19, 22, 33, 13};
int length = sizeof(ages)/sizeof(int);
printf("length = %d", length);
输出结果: 4
---
##3.练习
- 正序输出(遍历)数组
int ages[4] = {19, 22, 33, 13};
for (int i = 0; i < 4; i++) {
printf("ages[%d] = %d\n", i, ages[i]);
}
- 逆序输出(遍历)数组
int ages[4] = {19, 22, 33, 13};
for (int i = 3; i >=0; i--) {
printf("ages[%d] = %d\n", i, ages[i]);
}
- 从键盘输入数组长度,构建一个数组,然后再通过for循环从键 盘接收数字给数组初始化。并使用for循环输出查看
数组的内存分配
本小节知识点:
- 【掌握】数组内部存储细节
- 【理解】数组的地址
- 【理解】数组的越界问题
1.数组内部存储细节
-
存储方式:
- 1)计算机会给数组分配一块连续的存储空间
- 2)数组名代表数组的首地址,从首地址位置,依次存入数组的第1个、第2个....、第n个元素
- 3)每个元素占用相同的字节数(取决于数组类型)
- 4)并且数组中元素之间的地址是连续。
-
示例
模拟该数组的内存存储细节如下: int x[2]={1,2}; int ca[5]={'a','A','B','C','D'};
- 注意:字符在内存中是以对应ASCII值的二进制形式存储的,而非上表的形式。 在这个例子中,数组x的地址为它的首元素的地址0x08,数组ca的地址为0x03。
2.数组的地址
- 在内存中,内存从大到小进行寻址,为数组分配了存储空间后,数组的元素自然的从上往下排列 存储,整个数组的地址为首元素的地址。
- 数组a的地址是ffc1,a[0]的地址是ffc1,a[1]的地址是ffc5
- 因此a == &a[0],即第一个元素的地址就是整个数组的地址
3.数组的越界问题
- 数组越界导致的问题
- 约错对象
- 程序崩溃
char cs1[2] = {1, 2};
char cs2[3] = {3, 4, 5};
cs2[3] = 88; // 注意:这句访问到了不属于cs1的内存
printf("cs1[0] = %d\n", cs1[0] );
输出结果: 88
数组练习
1.设计一个函数int arrayMax(int a[], int count)找出数组元素的最大值
2.设计一个函数:int arraySum(int a[], int n),求一维数组a前n个数的和
3.写一个函数,可以将一维整型数组中的元素逆序存放。比如本来是1,3,4,2,逆序存放就变成了:2,4,3,1
数组元素作为函数参数
1.本小节知识点:
- 【掌握】数组元素作为函数参数
- 【掌握】数组名作为函数参数
- 【掌握】数组名作函数参数的注意点
- 数组可以作为函数的参数使用,进行数据传送。数组用作函数参数有两种形式:
- 一种是把数组元素(下标变量)作为实参使用
- 一种是把数组名作为函数的形参和实参使用
1.数组元素作为函数参数
-
数组元素就是下标变量,它与普通变量并无区别。 因此它作为函数实参使用与普通变量是完全相 同的,在发生函数调用时,把作为实参的数组元素的值传送给形参,实现单向的值传送。
-
数组的元素作为函数实参,与同类型的简单变量作为实参一样,是单向的值传递,即数组元素的值传给形参,形参的改变不影响实参
void change(int val)// int val = number { val = 55; } int main(int argc, const char * argv[]) { int ages[3] = {1, 5, 8}; printf("ages[0] = %d", ages[0]);// 1 change(ages[0]); printf("ages[0] = %d", ages[0]);// 1 }
- 用数组元素作函数参数不要求形参也必须是数组元素
2.数组名作为函数参数
-
在C语言中,数组名除作为变量的标识符之外,数组名还代表了该数组在内存中的起始地址, 因此,当数组名作函数参数时,实参与形参之间不是"值传递",而是"地址传递",实参数组名将 该数组的起始地址传递给形参数组,两个数组共享一段内存单元,编译系统不再为形参数组分配 存储单元。
-
数组的名字作为函数实参,传递的是整个数组,即形参数组和实参数组完全等同,是存放在同一存储空间的同一个数组。这样形参数组修改时,实参数组也同时被修改了。形参数组的元素个数可以省略
void change2(int array[3])// int array = 0ffd1 { array[0] = 88; } int main(int argc, const char * argv[]) { int ages[3] = {1, 5, 8}; printf("ages[0] = %d", ages[0]);// 1 change(ages[0]); printf("ages[0] = %d", ages[0]);// 88 }
3.数组名作函数参数的注意点
- 在函数形参表中,允许不给出形参数组的长度
void change2(int array[]) { array[0] = 88; }
- 形参数组和实参数组的类型必须一致,否则将引起错误。
void prtArray(double array[3]) // 错误写法 { for (int i = 0; i < 3; i++) { printf("array[%d], %f", i, array[i]); } } int main(int argc, const char * argv[]) { int ages[3] = {1, 5, 8}; prtArray(ages[0]); }
- 当数组名作为函数参数时, 因为自动转换为了指针类型,所以在函数中无法动态计算除数组的元素个数
void printArray(int array[]) { printf("printArray size = %lu\n", sizeof(array)); // 8 int length = sizeof(array)/ sizeof(int); // 2 printf("length = %d", length); }
冒泡排序
本小节知识点:
- 【了解】冒泡排序
- 【掌握】冒泡排序的步骤
- 【了解】练习
1.冒泡排序
-
冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复 地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列 的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来 是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
-
冒泡排序 分为: 大数下沉 小数上浮
2.冒泡排序
- 1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应 该会是最大的数。
- 3)针对所有的元素重复以上的步骤,除了最后一个。
-
4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
-
示例:
3.练习
- 输入一组无序数据,使用冒泡排序法进行排序,并输出。
选择排序
本小节知识点:
- 【了解】选择排序
- 【掌握】选择排序的基本思想
- 【了解】练习
1.选择排序
- 选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列 中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元 素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。
2.选择排序的基本思想
-
第一趟排序在所有待排序的n个记录中选出关键字最小的记录,将它与数据表中的第一个记录交 换位置,使关键字最小的记录处于数据表的最前端;第二趟在剩下的n-1个记录中再选出关键字最 小的记录,将其与数据表中的第二个记录交换位置,使关键字次小的记录处于数据表的第二个位 置;重复这样的操作,依次选出数据表中关键字第三小、第四小...的元素,将它们分别换到数据 表的第三、第四...个位置上。排序共进行n-1趟,最终可实现数据表的升序排列。
-
示例
3.练习
- 输入一组无序数据,使用选择排序法进行排序,并输出。
折半查找
本小节知识点:
- 【掌握】基本思路
- 【掌握】实现步骤
- 【了解】练习
1.基本思路
-
在有序表中,取中间元素作为比较对象,若给定值与中间元素的要查找的数相等,则查找成功; 若给定值小于中间元素的要查找的数,则在中间元素的左半区继续查找;
-
若给定值大于中间元素的要查找的数,则在中间元素的右半区继续查找。不断重复上述查找过 程,直到查找成功,或所查找的区域无数据元素,查找失败。
2.实现步骤
- 在有序表中,取中间元素作为比较对象,若给定值与中间元素的要查找的数相等,则查找成功;
- 若给定值小于中间元素的要查找的数,则在中间元素的左半区继续查找;
-
若给定值大于中间元素的要查找的数,则在中间元素的右半区继续查找。不断重复上述查找过 程,直到查找成功,或所查找的区域无数据元素,查找失败。
-
示例:
3.练习
- 输入一组有序数据,使用折半查找法查找一个数据,并输出其位 置。
进制转换查表法
void toBinary2(int num)
{
total(num, 1, 1);
}
void toOct2(int num)
{
total(num, 7, 3);
}
void toHex2(int num)
{
total(num, 15, 4);
}
void total(int num , int base, int offset)
{
// 1.定义表用于查询结果
char cs[] = {
'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
// 2.定义保存结果的数组
char rs[32];
// 计算最大的角标位置
int length = sizeof(rs)/sizeof(char);
int pos = length;//8
while (num != 0) {
int index = num & base;
rs[--pos] = cs[index];
num = num >> offset;
}
for (int i = pos; i < length; i++) {
printf("%c", rs[i]);
}
printf("\n");
}
void toBinary(int num)
{
// 1.定义表用于查询结果
char cs[] = {
'0', '1'
};
// 2.定义保存结果的数组
char rs[32];
// 计算最大的角标位置
int length = sizeof(rs)/sizeof(char);
int pos = length;//8
while (num != 0) {
int result = num & 1;
rs[--pos] = cs[result];
num = num >> 1;
}
for (int i = pos; i < length; i++) {
printf("%c", rs[i]);
}
}
void toOct(int num)
{
// 1.定义表用于查询结果
char cs[] = {
'0', '1', '2', '3', '4', '5',
'6', '7'
};
// 2.定义保存结果的数组
char rs[11];
// 计算最大的角标位置
int length = sizeof(rs)/sizeof(char);
int pos = length;//8
while (num != 0) {
int result = num & 7;
rs[--pos] = cs[result];
num = num >> 3;
}
for (int i = pos; i < length; i++) {
printf("%c", rs[i]);
}
}
void toHex(int num)
{
// 1.定义表用于查询结果
char cs[] = {
'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
// 2.定义保存结果的数组
char rs[8];
// 3.定义存储结果的索引
// 计算保存结果数组的元素个数
int length = sizeof(rs)/sizeof(char);
int pos = length;//8
while (num != 0) {
int result = num & 15;//12
//7 6
rs[--pos] = cs[result];//c 3
// pos--;
num = num >> 4;
}
for (int i = pos; i < length; i++) {
printf("%c", rs[i]);
}
}
void printHex(int num)
{
// 9 a b c d e f
for (int i = 0; i < 8; i++) {
int result = num & 15;
if (result > 9) {//10 11 1+
printf("%c", result - 10 + 'a');
}else{
printf("%d", result);
}
num = num >> 4;
}
}
void printOct(int num)
{
for (int i = 0; i < 11; i++) {
int result = num & 7;
printf("%d", result);
num = num >> 3;
}
}
void printBinary(int num)
{
// 要左移的位数
int temp = (sizeof(int) << 3) - 1;
while (temp >= 0) {
int result = (num >> temp) & 1;
printf("%d", result);
if (temp % 4 == 0) {
printf(" ");
}
temp--;
}
}