定义:数组是相同类型,有序数据的集合
下标或者索引代表了数组中元素距离第1个元素(首地址所在的元素)的偏移量。举例:第1个元素距离第1个元素的偏移量是0,所以数组中的下标是从0开始的。
数组的下标是从0开始的
数组的最大下标=数组的元素个数(数组的大小或容量)-1
int a:在内存空间中开辟一块内存,该空间的大小是4个字节。
int a1,a2,a3,a4,a5:在内存中开辟5块内存,该空间的大小是4个字节,不方便
int arr[5]:在内存中开辟连续的5块内存,每一块空间的大小是4个字节
语法:
数据类型 数组名[数组容量];
注意:数据类型又被叫类型说明符,数组容量又被叫做数组元素个数或者数组的大小长度
说明:
同一个数组中,所有元素的类型都是一致的。
数组名也是标识符,我们所说的数组(名),可以理解为数据类型是数组的变量(名)。命名规则与变量的规则一样,区别是变量使用单数,数组使用复数。
数组容量还可以叫做常量表达式。其值必须是整数。关于数组容量的类型:
#define SIZE 5
int size = 5;
int arr[5];
int arr[SIZE];
类型:
代表了数组中元素的类型,数组的空间大小=数组中所有元素空间之和
容量:
数组中能存储多少个元素,数组容量一定是一个整型。
深入理解:
① 定义一个数组,相当于申请了一个可以容纳所指定元素个数的内存单元。所申请的内存单元是连续的。
② 定义一个数组,相当于定义了多个匿名的变量,这些变量可以通过数组名[下标]
来访问。
范例:
//定义一个数组
int arr[10];
关于数组中元素默认值问题:
- 局部变量和static修饰的变量:元素的默认值是0
- 局部变量:元素的默认值是随机的,使用前必须初始化
- 举例:
#include
int g_age; // 全局作用域,定义在函数的外面,默认值是0 int g_ages[5];// 全局作用域,等同于全局变量,元素的默认值是0 int main(int argc, char *argv[]) { int p_age; // 局部作用域:函数中定义的所有的变量都属于局部作用域。变量使用前需要初始化。 int p_ages[5]; // 局部作用域:元素的值使用前需要初始化 return 0; } 注意:关于默认值,整形和浮点型的默认值是0,字符型的默认值是\0,\0的ASCII码是0
原则:数组中的元素不能一次性访问所有,只能一个一个访问
语法
取值
数组名[下标];
赋值
数组名[下标] = 值;
举例:
//定义一个存储10个元素的int数组
int arr[10];
//给数组的第一个元素赋值
arr[0]=88;
//访问数组中第一个元素
int a=arr[0];
//修改数组中第一个元素的值(覆盖)
arr[0]=66;
int c = arr[9];//如果是一个局部作用域的数组,此时元素值为随机数。全局作用域--值为0
int b = arr[10];//error,下标越界异常,访问了一个未知存储空间
#include
int main(int argc,char *argv[])
{
//创建数组,存放0~9
int arr[10];
//计算数组的大小:数组大小=数组所有元素所占字节总数/每一个元素的字节数,需要用sizeof
int len = sizeof(arr)/sizeof(arr[0]);
//通过for循环,给数组赋值0~9
for(int i=0;i<=9;i++)
{
arr[i]=i;
}
//逆序输出元素:数组最大下标=数组大小-1
//使用for循环获取数组每一个元素,称为数组的遍历
for(int j=len-1;j>=0;j--)
{
printf("%4d",arr[j]);
}
printf("\n");
return 0;
}
说明:所谓的初始化,就是定义数组的时候,用指定的数据给对应的元素赋值
语法:
数据类型 数组名[数组容量] = {…}
注意事项:
数组可以部分初始化:也就是可以给数组中的前几个元素初始化,未被初始化的元素将自动初始化,初始值是 0。(也就是一个数组中,一旦有元素被初始化,剩余元素将自动初始化为 0)
//部分初始化
int arr[10] = {11,12,13,14,15};//推荐写法,剩余默认为0,等价于
int arr[10] = {11,12,13,14,15,0,0,0,0,0};
char arr1[5] = {'a','b','c'};//推荐写法,剩余默认'\0',等价于
char arr1[5] = {'a','b','c','\0','\0'}; //等价于
char arr1[5] = {'a','b','c','0','0'};
char arr2[5] = {}; //等价于
char arr2[5] = {0}; //等价于
char arr2[5] = {0,0,0,0,0};
数组根据初始化的元素自动分配大小:若定义数组时未指定容量,则系统会根据初始化元素的个数来决定容量。
int arr[]={11,12,13,14,15}; //推荐写法,等价于
int arr[5]={11,12,13,14,15};
需求:从键盘输入年,月,日,计算并输出该日是该年的第几天
分析
代码:
#include
int main(int argc,char *argv[])
{
//首先创建一个数组用来存放每个月的天数,由于二月特殊,初始化使用平年
int t[]={31,28,31,30,31,30,31,31,30,31,30,31};
//从控制台输入年月日
int year,month,day;
printf("请输入年月日(MMMM-YY-dd):");
scanf("%d-%d-%d",&year,&month,&day);
//闰年校验,闰年则修改天数
if(year % 4==0 && year % 100!=0 || year % 400==0)
t[1]=29;
int sum = day;
//遍历数组,将输入的月前的每一个月天数相加记录到相应的变量中
for(int i=0;i < month-1;i++)
sum += t[i];
//打印输出
printf("%d月%d日是%d年的第%d天\n",month,day,year,sum);
return 0;
}
排序思想:向前冒泡
推理:
例如:将5,4,3,2,1
冒泡排序为1,2,3,4,5
排序演示:
第0轮:5,4,3,2,1 → 4,3,2,1,5
比较4次 = 数组长度5 - 轮数0 - 1
第1轮:4,3,2,1,5
→ 3,2,1,4,5
比较3次 = 数组长度5 - 轮数1 - 1
第2轮:3,2,1,4,5
→ 2,1,3,4,5
比较2次 = 数组长度5 - 轮数2 - 1
第3轮:2,1,3,4,5
→ 1,2,3,4,5
比较1次 = 数组长度5 - 轮数3 - 1
总结:
案例涉及到5个数的排序,排序了4轮,得到:轮数 = 元素个数(数组大小)- 1,我们可以通过一个外层for循环实现轮数的遍历。
案例涉及的每一轮中数列的排序次数,得到:次数 = 元素个数 - 轮数 [- 1],我们可以通过一个内层for循环实现每一轮次数的遍历。
每一次比较过程中,两个数涉及到位置交换,比如 a = 3, b = 4,交换ab的数据变为 a = 4,b = 3,应该如何实现:
引入一个临时变量temp,将a的值赋值给temp,int temp = a;
将b的值赋值给a, a = b;
将temp的值赋值给b, b = temp;
代码:
#include
int main(int argc,char *argv[])
{
// 创建一个数组,用来存放冒泡排序的数列
int arr[10];
// 定义循环变量和临时变量、是否升序排序,默认降序
int i,j,temp,desc = 1;
printf("请输入10个整数:\n");
// 计算数组的大小
int len = sizeof(arr) / sizeof(arr[0]);
// 通过控制台依次录入10个数字
for (i = 0; i < len; i++) scanf("%d", &arr[i]);// &arr[i]:[]的优先级高于&,[]跟arr结合,获取数组中的某个元素,再跟&结合,对这个元素取地址
printf("\n排序前:");
for (i = 0; i < len; i++) printf("%-4d", arr[i]);
printf("\n");
// 冒泡排序
// 外层循环:实现排序轮数的遍历:轮数 = 数组大小 - 1
for (i = 0; i < len - 1; i++)
{
// 设置一个flag,用来判断数组是否已经有序
int flag = 0;
// 内层循环:实现每一轮的比较次数:比较次数 = 数组大小 - 本轮轮数 - 1
for (j = 0; j < len - i -1; j++)
{
// 相邻的两个数比较后,如果满足交换条件,就要交换位置
// 降序排序: if(arr[j] < arr[j+1])
// 升序排序: if(arr[j] > arr[j+1])
if (desc)
{
// 降序
if(arr[j] < arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = 1;
}
}
else
{
// 升序
if(arr[j] > arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = 1;
}
}
}
if (!flag) break; // 如果进行了1轮之后flag还是等于0,则说明排序数列已经有序,后续轮数不再继续
}
printf("\n排序后:");
for (i = 0; i < len; i++) printf("%-4d", arr[i]);
printf("\n");
return 0;
}
衍生:
冒泡排序 → 鸡尾酒排序、摇坠排序、摇床排序、搅拌排序…