c入门第十八篇——支持学生数的动态增长(链表,指针的典型应用)

数组最大的问题,就是不支持动态的扩缩容,它是静态内存分配的,一旦分配完成,其容量是固定的。为了支持学生的动态增长,这里可以引入链表。

链表

在C语言中,链表是一种常用的数据结构,它由一系列的节点组成,每个节点包含数据和指向下一个节点的指针。
链表的关键点:

  1. 节点(Node): 链表中的每个元素称为节点。每个节点通常包含两个部分:数据(Data)和指向另一个节点的指针(Next)。
  2. 头指针(Head Pointer):一个指针,它指向链表的第一个节点。这是链表的起点,通过头指针可以遍历整个链表。
  3. 尾节点(Tail Node):链表的最后一个节点,并不是一个独立的指针,而是链表中的一个节点,其指针部分指向NULL,表示链表的结束。
    c入门第十八篇——支持学生数的动态增长(链表,指针的典型应用)_第1张图片
    学生信息的结构体可以重新定义为:
typedef struct student {
    int id; // 学号
    char name[MAX_NAME_LEN]; // 姓名
    float score; // 成绩
    struct student *next; //这里不能够使用 Student *next原因是Student还未定义。
} Student;

链表实现完整代码

#include 
#include 
#include 
#include  // for access() function

#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;
}

Student *init_node(int id, char *name, int score)
{
    Student *new_s;

    new_s = malloc(sizeof(Student));
    if (new_s == NULL) {
        printf("malloc Student failed!\n");
        exit(-1);
    }
    new_s->next = NULL;
    new_s->id = id;
    strcpy(new_s->name, name);
    new_s->score = score;
    return new_s;
}

void add_student()
{
    Student s, *cur, *new_s;

    printf("Enter student ID: ");
    scanf("%d", &s.id);
    printf("Enter student name: ");
    scanf("%s", s.name);
    s.score = 0.0; // 初始成绩设置为0

    if (!check_if_student_exsit(s.id)) {
        cur = stu_db.header;
        if (cur) {
            while(cur) {
                if (cur->next == NULL) {
                    new_s = init_node(s.id, s.name, s.score);
                    cur->next = new_s;
                    break;
                }
                cur = cur->next;
            }
        } else {
            new_s = init_node(s.id, s.name, s.score);
            stu_db.header = new_s;
        }

        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()
{
    if(access(STUDENT_SYSTEM, F_OK) != 0) { // 文件不存在
        return 0;
    }

    FILE *fp = fopen(STUDENT_SYSTEM, "r");
    if (fp == NULL) {
        printf("fopen student_system failed!\n");
        return 1;
    }

#define BUF_SIZE 1024
    char 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场景中,使用的还是数组

你可能感兴趣的:(c语言入门,c语言,链表,网络,开发语言,数据结构,程序人生,算法)