200字
03.12 简易学生管理系统-数据结构与算法课堂项目
2026-03-12
2026-03-19

任务详情

任务描述

编写一个管理学生信息的小程序。

目的

掌握结构体的定义、初始化及成员访问(普通变量 + 指针方式);

熟练使用指针操作结构体数组,实现数组遍历、排序;

理解地址传递的本质,通过指针修改结构体成员(成绩转等级);

掌握自定义排序规则(按姓名字典序、按成绩降序)的实现方法。

功能要求

定义学生结构体 STU,包含以下成员:

姓名:char name[20](最多 19 个字符);

成绩:float score(百分制,0~100);

等级:char grade(等级制,A/B/C/D/F);

typedef struct {
char name[NAME_LEN]; // 姓名

...... //自行补充成绩、等级

} STU;

实现函数 void scoreToGrade(STU *stuArr, int n)

参数:结构体数组指针、学生人数;

功能:遍历结构体数组,将每个学生的百分制成绩转换为等级制并写入grade成员;

转换规则:≥90→'A',80~89→'B',70~79→'C',60~69→'D',<60→'F';

void scoreToGrade(STU *stuArr, int n) {
    STU *p = stuArr;  // 指针指向数组首元素

    for (int i = 0; i < n; i++, p++) {  // 指针遍历数组
        if (p->score >= 90) {
            p->grade = 'A';
        } 
        ...... // 自行补充
    }
}

实现函数 void sortByName(STU *stuArr, int n)

参数:结构体数组指针、学生人数

功能:用指针操作实现结构体数组按姓名字典序升序排序

void sortByName(STU *stuArr, int n) {
    STU temp;        // 临时结构体,用于交换
    STU *p1, *p2;    // 遍历指针

    for (int i = 0; i < n - 1; i++) {
        p1 = stuArr + i;  // 指向第 i 个元素

        for (int j = i + 1; j < n; j++) {
            p2 = stuArr + j;  // 指向第 j 个元素

            // 字典序:前 > 后 则交换
            ......  // 自行补充
        }
    }
}

实现函数 void sortByScore(STU *stuArr, int n)

参数:结构体数组指针、学生人数

功能:用指针操作实现结构体数组按成绩降序排序

void sortByScore(STU *stuArr, int n) {
    ......  // 可模仿上一个自行补充完成
}

实现函数 void printStuInfo(STU *stuArr, int n)

参数:结构体数组指针、学生人数;

功能:遍历并打印所有学生的姓名、成绩、等级信息,格式清晰;

主函数要求

输入学生人数(最多 50 人)和每个学生的姓名、成绩;

调用scoreToGrade转换等级;

分别调用sortByNamesortByScore排序,并打印排序后的结果;

所有函数参数均使用指针传递,禁止使用全局变量。

输入输出要求

输入

首先输入学生人数n(1≤n≤50),若人数不符合要求,输出输入非法!请输入1~%d之间的数;

依次输入n个学生的姓名(不含空格)、成绩(浮点数)(假设输入的数据无误);

输入样例

张三 85.5

输出

原始学生信息(含转换后的等级);

按姓名排序后的学生信息;

按成绩降序排序后的学生信息;

输出样例

张三 85.5 B

李四 92.0 A

编程要求

所有结构体数组的操作必须通过指针实现(禁止使用数组下标[]遍历,仅允许初始化 / 输入时用下标);

排序函数中交换结构体元素时,需通过结构体指针完成值拷贝

对输入的成绩做合法性校验(0≤score≤100),若输入非法成绩,提示并要求重新输入;

代码注释清晰,关键函数、关键逻辑需添加注释。

实验提示

结构体指针访问成员:stuPtr->name 等价于 (*stuPtr).name

字符串比较用strcmp函数(需包含<string.h>),字典序升序排序规则:strcmp(p1->name, p2->name) > 0 时交换;

冒泡排序中,遍历数组时用STU *p = stuArr + i 指向第i个元素;

成绩合法性校验:输入后判断score < 0 || score > 100,若非法则重新输入该学生信息。

代码示例

#include <stdio.h>// 引入标准输入输出库,为了使用 printf 和 scanf
#include <string.h>  // 引入字符串处理库,为了使用 strcmp 比较姓名
#include <stdlib.h>  // 引入标准库(虽然这里没用到动态分配,但习惯性引入也没错)
#define MAX_STU 50   // 宏定义:规定这个系统最多能装 50 个学生。方便以后一键修改上限
#define NAME_LEN 20  // 宏定义:规定每个学生姓名的最大长度为 20 个字符

// ==========================================
// 1. 定义学生结构体 (图纸)
// ==========================================
// 使用 typedef 给匿名的 struct 起个别名叫 STU,以后写代码就不需要每次都写 struct STU 了
typedef struct {
    char name[NAME_LEN]; // 字符数组,用来存放学生的姓名,最多 19 个可见字符 + 1个结束符 '\0'
    float score;         // 单精度浮点数,用来存放百分制成绩(如 85.5)
    char grade;          // 单个字符,用来存放计算出来的等级(如 'A', 'B')
} STU;

// ==========================================
// 2. 成绩转等级函数
// ==========================================
/**
功能:百分制成绩转等级制
@param stuArr 学生结构体数组指针(也就是数组的首地址)
@param n 学生人数
*/
void scoreToGrade(STU *stuArr, int n) {
    STU *p = stuArr; // 定义一个指针 p,让它一开始指向队伍里的第一个学生

    // 循环遍历队伍。i 控制循环次数;p++ 让指针每次往后移动一个学生的位置
    for(int i = 0; i < n; i++, p++) { 
        // 使用 p->score 提取当前学生的成绩,然后进行判断
        if(p->score >= 90){
            p->grade = 'A'; // 如果大于等于 90,就把字母 'A' 塞进当前学生的 grade 格子里
        }
        else if(p->score >= 80){
            p->grade = 'B';
        }
        else if(p->score >= 70){
            p->grade = 'C';
        }
        else if(p->score >= 60){
            p->grade = 'D';
        }
        else {
            p->grade = 'F'; // 低于 60 分不及格,给 'F'
        }
    }
}

// ==========================================
// 3. 按姓名升序排序函数 (冒泡排序)
// ==========================================
/**
功能:按姓名字典序升序排序(冒泡排序)
@param stuArr 学生结构体数组指针
@param n 学生人数
*/
void sortByName(STU *stuArr, int n) {
    STU temp;        // 定义一个临时的结构体变量,作为交换两个学生数据时的“中转站”
    STU *p1, *p2;    // 定义两个指针,分别用来指向要进行比较的两个学生

    // 外层循环:控制比较的轮数,n 个人只需要比 n-1 轮
    for (int i = 0; i < n - 1; i++) {
        p1 = stuArr + i; // p1 指向当前轮次确定的基础学生(通过基地址 + 偏移量 i 计算得出)

        // 内层循环:让 p1 指向的学生,跟排在他后面的所有学生逐一打招呼比较
        for (int j = i + 1; j < n; j++) {
            p2 = stuArr + j; // p2 指向 p1 后面的某个学生

            // strcmp 比较两个名字。如果 p1 的名字在字典中排在 p2 的后面(返回值 > 0)
            // 说明顺序反了(我们要升序),需要把他们俩换位置
            if(strcmp(p1->name, p2->name) > 0){
                // 核心交换逻辑:解引用(*)后,连名带分把整个结构体的数据完整互换
                temp = *p1; // 把 p1 学生的所有数据暂存到 temp
                *p1 = *p2;  // 把 p2 学生的所有数据覆盖掉 p1 所在位置的数据
                *p2 = temp; // 把 temp 里暂存的原 p1 数据放到 p2 所在位置
            }
        }
    }
}

// ==========================================
// 4. 按成绩降序排序函数 (冒泡排序)
// ==========================================
/**
功能:按成绩降序排序(冒泡排序)
@param stuArr 学生结构体数组指针
@param n 学生人数
*/
void sortByScore(STU *stuArr, int n) {
    STU temp;        // 同样的中转站
    STU *p1, *p2;    // 同样的两根比较指针

    // 嵌套循环逻辑与姓名排序完全一致
    for (int i = 0; i < n - 1; i++) {
        p1 = stuArr + i; 
        for (int j = i + 1; j < n; j++) {
            p2 = stuArr + j;

            // 区别在这里:比较的是成绩 (score)。
            // 因为要求降序(高分在前),所以如果前面的分数小于后面的分数,就必须交换。
            if(p1->score < p2->score){
                temp = *p1;
                *p1 = *p2;
                *p2 = temp;
            }
        }
    }
}

// ==========================================
// 5. 打印信息函数
// ==========================================
/**
功能:打印学生信息
@param stuArr 学生结构体数组指针
@param n 学生人数
*/
void printStuInfo(STU *stuArr, int n) {
    STU *p = stuArr; // 让指针 p 指向队伍开头

    // 遍历队伍
    for(int i = 0; i < n; i++, p++){
        // %s 对应姓名(字符串),%.1f 对应成绩(浮点数保留1位),%c 对应等级(字符),\n 负责换行
        printf("%s %.1f %c\n", p->name, p->score, p->grade);
    }
}

// ==========================================
// 6. 主函数
// ==========================================
int main() {
    STU stuArr[MAX_STU]; // 在内存里真正划出一块地盘,打造 50 个学生的储物柜
    int n;               // 定义变量 n,用来存储实际要录入的学生人数

    // ---------- 第 1 步:输入并校验人数 ----------
    // 读取用户输入的人数(注意加了 & 符取地址)
    scanf("%d", &n);
    // 如果输入的人数不合规(小于1或大于最大容量),程序提前结束
    if( n < 1 || n > 50 ){
        // 提示错误
        printf("输入非法!请输入1~50之间的数\n"); 
        return 0;
    }

    // ---------- 第 2 步:输入学生姓名和成绩 ----------
    STU *p = stuArr; // 定义指针 p 指向第一个学生的柜子
    // 循环录入 n 个学生的信息
    for(int i = 0; i < n; i++, p++){
        // 输入姓名。p->name 是字符数组,本身就是地址,所以不需要加 &
        scanf("%s", p->name);

        // 使用内部循环校验成绩输入的合法性
        do {
            // 输入成绩。score 是浮点数,必须加 & 获取其内存地址
            scanf("%f", &(p->score)); 
            // 校验:成绩不能是负数,也不能超过 100 分
            if((p->score < 0) || (p->score > 100)){
                printf("输入非法!请重新输入成绩\n");
            }          
        } while((p->score < 0) || (p->score > 100)); // 只要成绩非法,就要求重输
    }

    // ---------- 第 3 步:处理和输出 ----------
    // 1. 调用成绩转等级函数。直接传数组名 stuArr(即首地址),函数内部就能顺着地址改写 grade
    scoreToGrade(stuArr, n);

    // 2. 打印刚转换完等级的原始顺序信息
    printStuInfo(stuArr, n);

    // 3. 调用按姓名排序函数,原数组的物理顺序会被打乱并重新排列
    sortByName(stuArr, n);
    // 打印按姓名重新排列后的信息
    printStuInfo(stuArr, n);

    // 4. 调用按成绩排序函数,原数组再次被重新排列
    sortByScore(stuArr, n);
    // 打印按成绩降序排列后的最终信息
    printStuInfo(stuArr, n);    

    // 程序顺利结束
    return 0;
}

03.12 简易学生管理系统-数据结构与算法课堂项目
作者
Aeolus
发表于
2026-03-12
License
CC BY-NC-SA 4.0

评论