数组最大的问题,就是不支持动态的扩缩容,它是静态内存分配的,一旦分配完成,其容量是固定的。为了支持学生的动态增长,这里可以引入链表。
链表
在C语言中,链表是一种常用的数据结构,它由一系列的节点组成,每个节点包含数据和指向下一个节点的指针。
链表的关键点:
- 节点(Node): 链表中的每个元素称为节点。每个节点通常包含两个部分:数据(Data)和指向另一个节点的指针(Next)。
- 头指针(Head Pointer):一个指针,它指向链表的第一个节点。这是链表的起点,通过头指针可以遍历整个链表。
- 尾节点(Tail Node):链表的最后一个节点,并不是一个独立的指针,而是链表中的一个节点,其指针部分指向NULL,表示链表的结束。
学生信息的结构体可以重新定义为:
typedef struct student {int id; // 学号char name[MAX_NAME_LEN]; // 姓名float score; // 成绩struct student *next; //这里不能够使用 Student *next原因是Student还未定义。
} Student;
链表实现完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define MAX_NAME_LEN 50
#define STUDENT_SYSTEM "student_system"typedef struct student {int id; // 学号char name[MAX_NAME_LEN]; // 姓名float score; // 成绩struct student *next;
} Student;struct student_db {Student *header; // 后续指向学生信息链表头部的指针int student_count; // 学生数量
};struct student_db stu_db = {.header = NULL,.student_count = 0
};int write_student_info(Student *s)
{FILE *fp = fopen(STUDENT_SYSTEM, "a");if (fp == NULL) {printf("fopen student_system failed!\n");return 1;}fprintf(fp, "%-4d %-10s %-.2f\n", s->id, s->name, s->score);fclose(fp);return 0;
}int check_if_student_exsit(int id)
{Student *cur= stu_db.header;while(cur!= NULL) {if(cur->id == id) {return 1;}cur = cur->next;}return 0;
}void add_student()
{Student s, *cur, *tmp_s;printf("Enter student ID: ");scanf("%d", &s.id);printf("Enter student name: ");scanf("%s", s.name);s.score = 0.0; // 初始成绩设置为0if (!check_if_student_exsit(s.id)) {cur = stu_db.header;while(cur) {if (cur->next == NULL) {tmp_s = malloc(sizeof(Student));if (tmp_s == NULL) {printf("malloc Student failed!\n");exit(-1);}tmp_s->next = NULL;tmp_s->id = s.id;strcpy(tmp_s->name, s.name);tmp_s->score = s.score;cur->next = tmp_s;break;}cur = cur->next;}stu_db.student_count++;printf("Student added successfully, all student: %d!\n", stu_db.student_count);write_student_info(&s);} else {printf("student has in db, do nothing!\n");}
}void print_title()
{printf("%-4s %-10s %-5s\n", "ID", "Name", "Score");
}void display_all_students()
{Student *cur;printf("-------- All students info --------\n");if (stu_db.student_count == 0) {printf("No students!\n");} else {print_title();cur = stu_db.header;while (cur) {printf("%-4d %-10s %-.2f\n", cur->id, cur->name, cur->score);cur = cur->next;}}printf("-------- End -----------\n");
}void find_student_by_id()
{int id;printf("Enter student ID to search: ");scanf("%d", &id);Student *cur = stu_db.header;while (cur) {if (cur->id == id) {printf("%-4d %-10s %-.2f\n", cur->id, cur->name, cur->score);return;}cur = cur->next;}printf("Student with ID %d not found!\n", id);
}void find_student_by_name()
{int is_find = 0;char name[MAX_NAME_LEN];printf("Enter student name to search: ");scanf("%s", name);Student *cur = stu_db.header;while (cur) {if(strcmp(cur->name, name) == 0) {printf("%-4d %-10s %-.2f\n", cur->id, cur->name, cur->score);is_find = 1;}cur = cur->next;}if (is_find == 0) {printf("Student with name %s not found!\n", name);}
}void add_score()
{int id;float score;printf("Enter student ID: ");scanf("%d", &id);printf("Enter student score: ");scanf("%f", &score);Student *cur = stu_db.header;while (cur) {if(cur->id == id) {cur->score = score;printf("Score added successfully!\n");return;}cur = cur->next;}printf("Student with ID %d not found!\n", id);
}void display_average_score()
{float total = 0.0;Student *cur = stu_db.header;while (cur) {total += cur->score;cur = cur->next;}printf("Average score of all students: %.2f\n", total / stu_db.student_count);
}int init_student_info()
{FILE *fp = fopen(STUDENT_SYSTEM, "r");if (fp == NULL) {printf("fopen student_system failed!\n");return 1;}#define BUF_SIZE 1024char buf[BUF_SIZE];Student *s, *current;while(fgets(buf, BUF_SIZE - 1, fp) != NULL) {s = malloc(sizeof(Student));if (s == NULL) {printf("malloc Student failed!\n");exit(-1);}s->next = NULL;sscanf(buf, "%d %s %f\n", &s->id, s->name, &s->score);stu_db.student_count++;if (stu_db.header == NULL) {stu_db.header = s;current = s;} else {current->next = s;current = s;}}fclose(fp);return 0;}int main()
{int choice;int ret;ret = init_student_info();if (ret) {printf("init_student_info failed!\n");return 1;}display_all_students();do {printf("\nStudent Score Management System\n");printf("1. Add Student\n");printf("2. Display All Students\n");printf("3. Find Student by ID\n");printf("4. Find Student by Name\n");printf("5. Add Score\n");printf("6. Display Average Score\n");printf("7. Exit\n");printf("Enter your choice: ");scanf("%d", &choice);switch(choice) {case 1:add_student();break;case 2:display_all_students();break;case 3:find_student_by_id();break;case 4:find_student_by_name();break;case 5:add_score();break;case 6:display_average_score();break;case 7:printf("Exiting...\n");break;default:printf("Invalid choice!\n");}} while(choice != 7);return 0;
}
总结
通过链表重构学生成绩管理系统,学生上限数除了受限于内存,其他不受限制。链表相较于数组,它有灵活的扩展的优势,但是它的内存不是连续的,访问性能比不上数组。
虽然当前数组实现的查询,也是遍历数组,但是这里是可以进行排序优化查询的,但是链表不行。
在高性能转发场景中,比如dpdk场景中,使用的还是数组。