4、C++项目实战:学生成绩管理系统(数组与结构体)

说实话,数组和结构体这两个东西,单独拿出来讲谁都懂。但把它们放到一个真实的项目里,很多新手就开始犯迷糊了。我当年带团队的时候,面试过不少候选人,问起结构体数组怎么用,背得滚瓜烂熟。一让写个学生管理系统,立马卡壳。

今天这个项目,就是要把这两个知识点焊在一起。咱们做一个真正能跑起来的成绩管理系统。

项目需求拆解

先看看我们要做什么。一个学生成绩管理系统,核心功能其实就这几条:

  • 录入学生信息(学号、姓名、三门课成绩)
  • 计算每个学生的总分和平均分
  • 按总分排名
  • 查询某个学生的成绩
  • 显示所有学生的成绩单

听起来不复杂对吧?但这里有个坑——数据怎么组织。你想想看,每个学生有多个属性,而且我们要管理一堆学生。用数组存?用结构体存?还是两者结合?

核心思路:用结构体描述单个学生,用结构体数组管理所有学生。这是C/C++里最经典的数据组织方式。

定义学生结构体

我个人习惯,写项目第一步不是写main函数,而是先定义数据结构。数据模型定好了,代码怎么写都顺。

struct Student {
    int id;           // 学号
    char name[20];    // 姓名
    float chinese;    // 语文成绩
    float math;       // 数学成绩
    float english;    // 英语成绩
    float total;      // 总分
    float average;    // 平均分
};

这里有个细节我提一下。为什么total和average也放在结构体里?而不是临时计算?

嗯,这是性能上的考虑。如果学生数量很大(比如上千人),每次排序都重新计算总分,效率就低了。我在项目中遇到过类似场景——一个报表系统每次查询都实时计算,结果用户等得直拍桌子。后来改成预计算字段,速度直接翻倍。

用数组管理多个学生

结构体定义好了,接下来就是声明一个数组来装学生:

#define MAX_STUDENTS 100

Student students[MAX_STUDENTS];  // 最多管理100个学生
int studentCount = 0;            // 当前学生数量

说白了,这就是一个「结构体数组」。每个元素是一个Student结构体,里面装着这个学生的所有信息。

小技巧:用#define定义最大容量,而不是写死数字。这样后期改容量只需要改一处,不会漏掉。

核心功能实现

1. 录入学生信息

录入的时候要注意什么?我踩过最大的坑就是——忘记检查数组是否已满。曾经有个项目上线后,用户录到第101个学生时程序直接崩溃,那叫一个尴尬。

void addStudent(Student students[], int &count) {
    if (count >= MAX_STUDENTS) {
        cout << "学生数量已满,无法继续录入!" << endl;
        return;
    }
    
    Student s;
    cout << "请输入学号: ";
    cin >> s.id;
    cout << "请输入姓名: ";
    cin >> s.name;
    cout << "请输入语文成绩: ";
    cin >> s.chinese;
    cout << "请输入数学成绩: ";
    cin >> s.math;
    cout << "请输入英语成绩: ";
    cin >> s.english;
    
    // 计算总分和平均分
    s.total = s.chinese + s.math + s.english;
    s.average = s.total / 3.0;
    
    students[count] = s;
    count++;
    
    cout << "录入成功!" << endl;
}

注意这里count用的是引用传递。为什么?因为我们要修改它的值。如果传值进去,函数里改了count,外面根本不知道——这个坑我见过太多次了。

2. 按总分排名(冒泡排序)

排名嘛,说白了就是排序。这里我用冒泡排序,因为逻辑最直观,适合教学。实际项目中我会用sort函数,但咱们现在练的是基本功。

void sortByTotal(Student students[], int count) {
    for (int i = 0; i < count - 1; i++) {
        for (int j = 0; j < count - 1 - i; j++) {
            if (students[j].total < students[j + 1].total) {
                // 交换整个结构体
                Student temp = students[j];
                students[j] = students[j + 1];
                students[j + 1] = temp;
            }
        }
    }
}

看到没?交换的是整个结构体,而不是只交换总分。这一点很多新手会搞错——只换了分数,结果学号和姓名对不上,数据全乱了。

注意:结构体赋值是深拷贝。如果结构体里有指针成员,直接赋值会出问题。但咱们这里只有基本类型和字符数组,没问题。

3. 查询学生信息

查询功能,我习惯按学号查。因为学号是唯一的,不会重名。

void searchById(Student students[], int count, int targetId) {
    for (int i = 0; i < count; i++) {
        if (students[i].id == targetId) {
            cout << "学号: " << students[i].id << endl;
            cout << "姓名: " << students[i].name << endl;
            cout << "语文: " << students[i].chinese << endl;
            cout << "数学: " << students[i].math << endl;
            cout << "英语: " << students[i].english << endl;
            cout << "总分: " << students[i].total << endl;
            cout << "平均分: " << students[i].average << endl;
            return;
        }
    }
    cout << "未找到该学号的学生!" << endl;
}

这里有个小细节——找到后直接return,不要再继续循环了。效率问题,虽然学生少的时候无所谓,但养成好习惯总没错。

知识体系总览

下面这张图,是我梳理的本章知识结构。你看一眼就能明白,数组和结构体是怎么配合的。

学生成绩管理系统 · 知识结构图 struct Student 结构体 Student students[100] 结构体数组 录入学生信息 计算总分平均分 按总分排序 按学号查询 数组越界检查 预计算 vs 实时计算 结构体整体交换 引用传递参数 核心:结构体描述个体,数组管理群体

完整代码框架

最后,我把整个程序的骨架贴出来。你照着这个结构去填充,就能跑起来。

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

struct Student {
    int id;
    char name[20];
    float chinese, math, english;
    float total, average;
};

#define MAX_STUDENTS 100

// 函数声明
void addStudent(Student[], int &);
void sortByTotal(Student[], int);
void searchById(Student[], int, int);
void displayAll(Student[], int);

int main() {
    Student students[MAX_STUDENTS];
    int count = 0;
    int choice;
    
    do {
        cout << "\n===== 学生成绩管理系统 =====\n";
        cout << "1. 录入学生信息\n";
        cout << "2. 按总分排名\n";
        cout << "3. 查询学生\n";
        cout << "4. 显示所有学生\n";
        cout << "0. 退出\n";
        cout << "请选择: ";
        cin >> choice;
        
        switch(choice) {
            case 1: addStudent(students, count); break;
            case 2: sortByTotal(students, count); break;
            case 3: {
                int id;
                cout << "请输入学号: ";
                cin >> id;
                searchById(students, count, id);
                break;
            }
            case 4: displayAll(students, count); break;
        }
    } while(choice != 0);
    
    return 0;
}

我的建议:先别急着抄代码。自己动手把每个函数实现一遍,遇到卡住的地方再回来看。这样印象最深。

这个项目虽然小,但五脏俱全。它教会你的不只是语法,更是一种思维方式——用结构体封装数据,用数组管理批量数据。这种模式,在后面的链表、文件操作、数据库编程里都会反复出现。

好了,动手试试吧。有问题随时翻回来看。


公众号:蓝海资料掘金营,微信deep3321