C++ 系统学习日记・第 07 天|数组全解:一维数组 + 冒泡排序 + 二维数组 + 实战案例

5 阅读8分钟

📖 学习信息

学习课程:黑马 C++ 零基础入门教程

本日学习:C++ 数组完整知识点包含:一维数组(定义 + 初始化 + 常见操作)、冒泡排序算法、二维数组(定义 + 初始化 + 遍历)、数组注意事项、综合实战案例


一、前言

数组是 C++ 中存储同类型数据的连续内存集合,是最基础的数据结构之一。当需要存储大量相同类型的数据(比如学生成绩、员工工资、游戏坐标)时,使用数组可以避免定义大量零散变量,极大提高代码的整洁性和可维护性。数组也是后续学习指针、字符串、容器、算法的基础,必须彻底掌握。


二、一维数组

一维数组是最简单的数组形式,用于存储一行同类型的数据。

1. 一维数组的定义

语法

数据类型 数组名[数组长度];
  • 数据类型:数组中每个元素的类型(int、double、char 等)
  • 数组名:数组的标识符,遵循变量命名规范
  • 数组长度:数组能存储的元素个数,必须是常量表达式(不能是变量)

示例

// 定义一个长度为5的整型数组,存储5个整数
int arr[5];
// 定义一个长度为10的浮点型数组
double scores[10];

2. 一维数组的初始化

数组初始化有三种常用方式,未初始化的数组元素值是随机的,建议养成初始化习惯。

// 1. 全部初始化:给所有元素赋值
int arr1[5] = {1, 2, 3, 4, 5};

// 2. 部分初始化:只给前n个元素赋值,剩余元素自动初始化为0
int arr2[5] = {1, 2, 3};  // 等价于 {1,2,3,0,0}

// 3. 省略数组长度:编译器自动根据初始化值的个数确定长度
int arr3[] = {1, 2, 3, 4};  // 数组长度为4

3. 数组元素的访问

通过下标访问数组元素,数组下标从 0 开始,最大下标为「数组长度 - 1」。

int arr[5] = {10, 20, 30, 40, 50};

// 访问第一个元素
cout << arr[0] << endl;  // 输出10
// 访问第三个元素
cout << arr[2] << endl;  // 输出30

// 修改元素值
arr[1] = 200;
cout << arr[1] << endl;  // 输出200

4. 一维数组的常见操作

(1)数组遍历(最常用)

通过循环遍历数组的所有元素,是数组所有操作的基础。

int arr[5] = {1, 2, 3, 4, 5};
// 遍历数组,输出所有元素
for (int i = 0; i < 5; i++) {
    cout << arr[i] << " ";
}

(2)数组求和

int arr[5] = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 0; i < 5; i++) {
    sum += arr[i];
}
cout << "数组元素和为:" << sum << endl;  // 输出15

(3)求数组最大值

int arr[5] = {12, 45, 7, 89, 23};
int max = arr[0];  // 假设第一个元素是最大值
for (int i = 1; i < 5; i++) {
    if (arr[i] > max) {
        max = arr[i];
    }
}
cout << "数组最大值为:" << max << endl;  // 输出89

(4)数组逆序

将数组元素的顺序反转,比如 {1,2,3,4,5} → {5,4,3,2,1}

int arr[5] = {1, 2, 3, 4, 5};
// 首尾元素交换,循环到数组中间即可
for (int i = 0; i < 5/2; i++) {
    int temp = arr[i];
    arr[i] = arr[4 - i];
    arr[4 - i] = temp;
}
// 输出逆序后的数组
for (int i = 0; i < 5; i++) {
    cout << arr[i] << " ";
}

三、重点算法:冒泡排序

冒泡排序是最基础的排序算法,核心思想是相邻元素两两比较,大的元素往后 “冒泡” ,每一轮排序都会将当前未排序部分的最大元素放到正确位置。

1. 算法原理

  • 对于长度为 n 的数组,需要进行 n-1 轮 排序
  • 每一轮排序中,需要进行 n-1-i 次 比较(i 为当前轮数,从 0 开始)
  • 每次比较相邻两个元素,如果前一个大于后一个,就交换它们的位置

2. 完整代码实现

#include <iostream>
using namespace std;

int main()
{
    int arr[6] = {4, 2, 8, 0, 5, 7};
    int len = sizeof(arr) / sizeof(arr[0]);  // 计算数组长度

    cout << "排序前:";
    for (int i = 0; i < len; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    // 冒泡排序核心
    for (int i = 0; i < len - 1; i++) {  // 外层循环:控制排序轮数
        for (int j = 0; j < len - 1 - i; j++) {  // 内层循环:控制每轮比较次数
            if (arr[j] > arr[j + 1]) {  // 前一个元素大于后一个,交换
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }

    cout << "排序后:";
    for (int i = 0; i < len; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    return 0;
}

3. 运行结果

排序前:4 2 8 0 5 7 
排序后:0 2 4 5 7 8 

四、二维数组

二维数组用于存储多行多列的同类型数据,比如学生的多门成绩、矩阵数据等,可以理解为 “数组的数组”。

1. 二维数组的定义

语法

数据类型 数组名[行数][列数];

示例

// 定义一个3行4列的整型数组,存储3个学生的4门成绩
int scores[3][4];

2. 二维数组的初始化

// 1. 全部初始化
int arr1[2][3] = {{1, 2, 3}, {4, 5, 6}};

// 2. 按行部分初始化,剩余元素自动为0
int arr2[2][3] = {{1, 2}, {4}};  // 等价于 {{1,2,0}, {4,0,0}}

// 3. 省略行数,编译器自动根据初始化值确定行数
int arr3[][3] = {1, 2, 3, 4, 5, 6};  // 等价于 2行3列

// 4. 省略列数(不推荐,编译器无法确定)
// int arr4[2][] = {1,2,3,4};  // 错误写法

3. 二维数组的访问与遍历

通过行下标和列下标访问元素,行下标和列下标都从 0 开始。遍历二维数组需要使用嵌套循环:外层循环控制行,内层循环控制列。

int scores[3][4] = {
    {90, 85, 95, 88},  // 第1个学生的4门成绩
    {78, 82, 80, 75},  // 第2个学生的4门成绩
    {92, 98, 89, 96}   // 第3个学生的4门成绩
};

// 遍历二维数组,输出所有成绩
for (int i = 0; i < 3; i++) {  // 外层循环:遍历行
    cout << "第" << i+1 << "个学生成绩:";
    for (int j = 0; j < 4; j++) {  // 内层循环:遍历列
        cout << scores[i][j] << " ";
    }
    cout << endl;
}

4. 二维数组常见操作:计算每个学生的总分

int scores[3][4] = {
    {90, 85, 95, 88},
    {78, 82, 80, 75},
    {92, 98, 89, 96}
};

for (int i = 0; i < 3; i++) {
    int sum = 0;
    for (int j = 0; j < 4; j++) {
        sum += scores[i][j];
    }
    cout << "第" << i+1 << "个学生总分:" << sum << endl;
}

五、数组的核心注意事项与避坑点

  1. **数组下标越界(最严重错误)**数组下标范围是 0 ~ 长度-1,访问超出范围的下标会导致程序崩溃或数据异常

    int arr[5] = {1,2,3,4,5};
    cout << arr[5] << endl;  // 错误:下标越界,最大下标是4
    
  2. 数组长度必须是常量表达式C++ 标准不支持用变量作为数组长度(部分编译器支持,但不推荐)

    int n = 5;
    int arr[n];  // 错误:n是变量,不是常量
    
  3. 数组名的本质数组名是指向数组首元素的常量指针,不能被赋值

    int arr[5] = {1,2,3,4,5};
    int *p = arr;  // 正确:arr指向首元素arr[0]
    // arr = p;    // 错误:数组名是常量,不能赋值
    
  4. 不能直接赋值整个数组只能逐个元素赋值,不能直接用 arr1 = arr2 赋值

    int arr1[5] = {1,2,3,4,5};
    int arr2[5];
    // arr2 = arr1;  // 错误
    // 正确:逐个赋值
    for (int i = 0; i < 5; i++) {
        arr2[i] = arr1[i];
    }
    
  5. sizeof 计算数组长度sizeof(数组名) 得到数组占用的总字节数,除以单个元素的字节数得到数组长度

    int arr[5] = {1,2,3,4,5};
    int len = sizeof(arr) / sizeof(arr[0]);  // 5
    

六、综合实战案例:学生成绩管理系统

结合一维数组和二维数组,实现一个简单的学生成绩管理系统,支持输入成绩、计算总分、平均分、排名:

#include <iostream>
#include <string>
using namespace std;

int main()
{
    const int STUDENT_NUM = 3;  // 学生人数
    const int SUBJECT_NUM = 3;  // 科目数

    string names[STUDENT_NUM];  // 存储学生姓名
    int scores[STUDENT_NUM][SUBJECT_NUM];  // 存储成绩
    int totalScores[STUDENT_NUM] = {0};  // 存储每个学生的总分

    // 输入学生信息和成绩
    for (int i = 0; i < STUDENT_NUM; i++) {
        cout << "请输入第" << i+1 << "个学生姓名:";
        cin >> names[i];
        cout << "请输入" << names[i] << "的语文、数学、英语成绩(用空格分隔):";
        for (int j = 0; j < SUBJECT_NUM; j++) {
            cin >> scores[i][j];
            totalScores[i] += scores[i][j];
        }
    }

    // 输出成绩统计
    cout << "\n===== 成绩统计 =====\n";
    cout << "姓名\t语文\t数学\t英语\t总分\t平均分\n";
    for (int i = 0; i < STUDENT_NUM; i++) {
        double avg = totalScores[i] / (double)SUBJECT_NUM;
        cout << names[i] << "\t"
             << scores[i][0] << "\t"
             << scores[i][1] << "\t"
             << scores[i][2] << "\t"
             << totalScores[i] << "\t"
             << avg << endl;
    }

    return 0;
}

七、今日学习总结

  1. 一维数组

    • 定义:数据类型 数组名[长度];,长度必须是常量
    • 初始化:全部初始化、部分初始化、省略长度初始化
    • 常见操作:遍历、求和、求最值、逆序
    • 核心算法:冒泡排序(n-1 轮,每轮 n-1-i 次比较)
  2. 二维数组

    • 定义:数据类型 数组名[行数][列数];
    • 初始化:按行初始化、省略行数初始化
    • 遍历:嵌套循环,外层控制行,内层控制列
  3. 数组注意事项

    • 下标从 0 开始,避免越界
    • 数组名是常量指针,不能赋值
    • 不能直接赋值整个数组
    • sizeof(数组名)/sizeof(元素) 计算数组长度

✍️ 下节预告

下一篇将正式进入 C++ 函数核心基础:函数的定义、函数的调用、值传递、函数的声明、函数的分文件编写,掌握函数可以将复杂程序拆分为独立模块,提高代码复用性和可维护性,为后续学习函数高级特性和面向对象编程打下坚实基础。