详细介绍:C语言 :一维数组和冒泡排序

内容提要

  • 数组

    • 数组概念

    • 一维数组

    • 排序算法:冒泡排序

数组

数组的引入

  • 如果要在程序中保存一个人的年龄?如何保存?

    创建一个基于int类型的变量,例如:int age = 21;

  • 如果要在程序中保存一个人的三门课的成绩?如何保存?

    创建三个基于float 类型的变量,举例:float score1,score2,score3;

  • 保存一个人的15门的课程成绩?如何保存?

    • 用数组保存

数组的概念

什么是数组

定义:数组是相同类型有序数据的集合

数组的特征
  • 数组中的数据被称为数组的元素(所谓的元素,就是数组的每一个匿名的变量空间) ,是同构。

  • 数组中的元素存放在内存空间中(char play_name[6]:申请在内存中开辟6块连续的基于char 型的变量空间)

衍生概念:下标(索引)
  • 下标或者索引代表了数组中元素距离第一个元素(首地址所在的元素)的偏移量。举例:第一个元素举例第一个元素的偏移量是0,所以数组中的下标从0开始。

  • 数组的下标是从0开始的:

    数组的最大下标=数组的元素个数(数组的大小或容量)-1

    int a:在内存中开辟一块空间,该空间的大小是4个字节。

    int a1,a2,a3,a4,a5:在内存中开辟连续的五块空间。每一块空间的大小是4个字节。

    int arr[5]:在内存中开辟连续的5块空间,每一块空间的大小是4个字节。

一维数组

数组的定义

语法

数据类型 数组名 [数组容量];

注意:数据类型又被称作类型说明符,数组容量又被称作数组元素个数或者数组大大小/长度

说明

  • 数组的数据类型由数组中的元素来决定。也就是说数据类型,由元素的类型来决定,元素是什么类型,数组就是什么类型。同一个数组中,所有元素的类型都是一致的。

  • 数组名也是标识符,我们所说的数组(名),大家可以理解为数据类型是数组的变量(名)。命名规则与变量的规则一样,唯一的区别是变量使用单数,数组使用复数。

  • 也就是以字母或者下划线开头,后面只能跟字母、数字、下划线。

  • 数组容量还可以叫做常量表达式。其值必须是整数。关于数组容量的类型:

    • C89标准:只支持常量和符号常量,不支持变量。

#define SIZE 5 // 符号常量,使用宏定义
int lenght = 5; // 变量
int arr1[5]; // 常量(字面量)正确
int arr2[SIZE]; // 符号常量,正确
int arr3[length];// 变量,C89错误

C99标准(大部分环境支持):引入变长数组(VLA)的概念,就是可以使用变

量,数组在运行的时候决定大小。举例:

int length = 5; // length = 5; 创建一个变量length,赋值5
int arr[length]; // 创建一个容量为5的数组,C99标准下是正确的(执行这句的代码的时候,已经在内存申请空间)
length = 10; // 此时虽然变量length的值变成了10,但是已经创建的数组的大小是不会改变的。数组大小依然是5

类型:

代表了数组中元素的类型,数组的空间大小=数组中所有元素空间之和。

容量:

数组中能存储多少个元素,数组容量一定是一个整型。

深入理解:

①定义一个数组,相当于申请了一个可以容纳指定元素个数的内存单元。所申请的内存单元是连续的

②定义一个数组,相当于定义了多个匿名的变量,这些变量可以通过数组名[下标]来访问。

范例

//定义一个数组
int arr[10];     //定义一个存放10个int类型元素的数组

关于数组中元素默认值问题:

  • 全局变量和static修饰的变量:元素的默认值是0

  • 局部变量:元素的默认值是随机值,建议初始化。

举例:

int g_age;  //全局变量,定义在函数的外面,默认值位0
  int g_ages[5]; //全局数组,等同于全局变量,数组中元素的默认值为0
 ​
  int main()
  {
      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;//使用66覆盖88
​
int c = arr[9];//局部作用域的数组访问值为随机数,全局作用域访问为0
int b == arr[10];//error,下标越界异常,访问了一个未知的存储空间

案例

  • 需求:利用循环给数组元素a[0]~a[9]赋值 0~9 ,并且要求逆序输出。

  • 代码:

    #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 = 0; j--) printf("%4d", arr[j]);
    printf("\n");
    return 0;
    }
数组的初始化

说明:定义数组的时候,用指定的数据给对应的元素赋值。

语法

数据类型 数组名[数组容量] = {...};

注意事项:

  • 数组可以部分初始化:也就是可以给数组中的前几个元素初始化,未被初始化的元素系统将自动初始化,初始值是0。(一个数组中一旦有元素初始化,剩余元素将自动初始化为0)

int arr[10] = {11,12,13,14,15}; //部分初始化
​
char arr1[5] = {'a','b','c'};   //部分初始化,等价于
char arr1[5] ={'a','b','c','\0','\0'};  //字符类型自动填充'\0'
char arr1[5] ={'a','b','c','0','0'}; //'\0'对应的ASCII码是'0'
​
int arr2[5] = {};// 等价于下面三种写法
int arr2[5] = {0};
int arr2[5] = {0,0,0,0,0};
  • 数组根据初始化的元素自动分配大小:如果定义数组是未定义数组容量,则系统会根据初始化的元素的个数来决定容量。

    int arr[] ={1,2,3,4,5};
     int arr[5] = {1,2,3,4,5};
一维数组案例

案例1:

  • 求斐波拉契数列,限制在20个。

  • 代码:

    #include
    int main(int argc,char *argv[])
    {
    // 定义循环变量
    int i;
    // 定义一个数组,用来存储20个数列
    int f[20] = {1,1};
    // 计算数组的大小
    int len = sizeof(f) / sizeof(f[0]);
    // 通过一个for循环完成数列
    for (i = 2; i  0 && i % 5 == 0) printf("\n");
    printf("%8d",f[i]);
    }
    printf("\n");
    return 0;
    }

案例2:

  • 从键盘输入年,月,日计算并该日是该年第几天。

  • 分析:

    • 首先创建一个数组,用来存放每一个月的天数,因为二月特殊,初始化的时候默认为平年的天数。

    • 从控制台输入年,月,日

    • 闰年校验:如果是闰年,就修改数组中二月份对应的天数。

    • 定义一个变量,用来记录天数,默认就是我们输入的天数。

    • 遍历数组,将输入的月份之前的每一个月的天数取出来加到巨鹿天数的变量中。

    • 将统计后天数打印输出。

  • 代码:

    #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("请输入年份、月份、天(yyyy-MM-dd):");
    scanf("%d-%d-%d", &year, &month, &day);
    // 闰年校验:如果是闰年,就修改数组中二月份对应的天数(平年:28天,闰年:29天)。
    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;
    }

冒泡排序

  • 排序思想(向前冒泡):

    • 一次只排好一个元素,针对n个元素,最差情况需要n-1次可以排好。最好情况需要交换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  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;
    }
    }
    }
    if (!flag) break; // 如果进行了1轮之后flag还是等于0,则说明
    排序数列已经有序,后续轮数不再继续
    }
    printf("\n排序后:");
    for (i = 0; i < len; i++) printf("%-4d", arr[i]);
    printf("\n");
    return 0;
    }

posted @ 2025-07-26 09:45  wzzkaifa  阅读(19)  评论(0)    收藏  举报