c语言链表-学生管理系统

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义结构体
struct STU {
    char num[8];   // 学号
    char name[5];  // 姓名
    int score;     // 成绩
};

// 定义链表
struct temp {
    struct STU* s;
    struct temp* next;
};

void add(struct temp** head);   // 插入信息
void del(struct temp** head);   // 删除信息
void mod(struct temp* head);    // 修改信息
void find(struct temp* head);   // 查找信息
void app(struct temp* head);    // 显示所有信息

// 主函数
int main() {
    struct temp* head = NULL;
    while (1) {
        printf("学生信息管理系统\n");
        printf("请选择:\n");
        printf("********** 1、输入信息**********\n");
        printf("********** 2、删除信息**********\n");
        printf("********** 3、修改信息**********\n");
        printf("********** 4、查找信息**********\n");
        printf("********** 5、显示全部信息******\n");
        printf("********** 6、退出**************\n");
        int choice = 0;
        scanf_s("%d", &choice);
        switch (choice) {
        case 1:
            add(&head);
            break;
        case 2:
            del(&head);
            break;
        case 3:
            mod(head);
            break;
        case 4:
            find(head);
            break;
        case 5:
            app(head);
            break;
        case 6:
            return 0;
        default:
            printf("无效选项,请重新选择\n");
        }
    }
    return 0;
}

// 增加
void add(struct temp** head) {
    struct temp* new_node = (struct temp*)malloc(sizeof(struct temp)); // 分配新节点内存
    new_node->s = (struct STU*)malloc(sizeof(struct STU));             // 分配学生信息内存
    new_node->next = NULL;                                             // 初始化 next 指针为 NULL

    printf("输入学号:\n");
    scanf_s("%s", new_node->s->num, sizeof(new_node->s->num));  // num是数组名,不需要用符号&
    printf("输入姓名:\n");
    scanf_s("%s", new_node->s->name, sizeof(new_node->s->name)); // name是数组名,不需要用符号&
    printf("输入成绩:\n");
    scanf_s("%d", &new_node->s->score);

    // 将新节点添加到链表末尾
    if (*head == NULL) {
        *head = new_node;
    }
    else {
        struct temp* pr = *head;
        while (pr->next != NULL) {
            pr = pr->next;
        }
        pr->next = new_node;
    }
}

// 删除
void del(struct temp** head) {
    if (*head == NULL) {
        printf("链表为空\n");
        return;
    }

    char num[8] = { 0 };
    printf("输入学号\n");
    scanf_s("%s", num, sizeof(num));

    struct temp* pr = NULL; // 用于追踪前一个节点
    struct temp* p = *head;

    while (p != NULL && strcmp(p->s->num, num) != 0) {
        pr = p;   // pr是p的前一个节点
        p = p->next;
    }

    if (p == NULL) {
        printf("未找到该学号的学生\n");
        return;
    }

    if (p == *head) {
        *head = p->next; // 如果删除的是头节点
        printf("删除成功\n");
    }
    else {
        pr->next = p->next; // 从链表中跳过要删除的节点
    }
    free(p->s); // 释放学生信息内存
    free(p); // 释放节点内存
    printf("删除成功\n");
}

// 修改
void mod(struct temp* head) {
    if (head == NULL) {
        printf("链表为空\n");
        return;
    }

    char num[8] = { 0 };
    printf("输入学号:\n");
    scanf_s("%s", num, sizeof(num));

    struct temp* p = head;
    while (p != NULL && strcmp(p->s->num, num) != 0) {
        p = p->next;
    }

    if (p == NULL) {
        printf("未找到该学号的学生\n");
        return;
    }

    printf("选择修改的信息\n");
    printf("1.学号\n");
    printf("2.姓名\n");
    printf("3.成绩\n");
    printf("4.退出\n");
    printf("**************************\n");
    int c = 0;
    scanf_s("%d", &c);

    switch (c) {
    case 1:
        printf("输入修改后的学号:\n");
        scanf_s("%s", p->s->num, sizeof(p->s->num));
        printf("修改成功\n");
        break;
    case 2:
        printf("输入修改后的姓名:\n");
        scanf_s("%s", p->s->name, sizeof(p->s->name));
        printf("修改成功\n");
        break;
    case 3:
        printf("输入修改后的成绩:\n");
        scanf_s("%d", &p->s->score);
        printf("修改成功\n");
        break;
    case 4:
        return;
    default:
        printf("不存在该选项,请重新选择\n");
    }
}

// 查找
void find(struct temp* head) {
    if (head == NULL) {
        printf("链表为空\n");
        return;
    }

    char num[8] = { 0 };
    printf("输入学号:\n");
    scanf_s("%s", num, sizeof(num));

    struct temp* p = head;
    while (p != NULL && strcmp(p->s->num, num) != 0) {
        p = p->next;
    }

    if (p != NULL) {
        printf("学号:%s\t 姓名:%s\t 成绩:%d\n", p->s->num, p->s->name, p->s->score);
    }
    else {
        printf("未找到该学号的学生\n");
    }
}

// 显示
void app(struct temp* head) {
    if (head == NULL) {
        printf("链表为空,没有学生信息。\n");
        return;    // 提前返回,避免后续操作
    }

    int n = 0;
    struct temp* p = head;
    while (p != NULL) {
        if (p->s != NULL) {   // 确保指针 s 不是 NULL
            n++;
            printf("学号:%s\t 姓名:%s\t 成绩:%d\n", p->s->num, p->s->name, p->s->score);
        }
        p = p->next;
    }
    printf("总共有 %d 名学生的信息\n", n);
}

//因为在输入姓名和学号时用的是%s,即字符串,所以name[5]实际上只有4个数组元素和一个\0;num[8]有7个数组元素和一个\0。
// 所以,该程序输入学号应该是7位。

(一)、复习字符串和数组:
在C语言中,字符串和数组的长度定义以及存储\0(空字符)的方式是非常重要的。以下是对你问题的详细解答:

  1. 字符串长度为5,是否包含\0
    在C语言中,字符串是以字符数组的形式表示的,并且以一个特殊的空字符\0(NUL字符)结尾。这个\0用于标识字符串的结束。因此,如果你定义一个字符数组的长度为5,这个数组通常可以存储最多4个可见字符和1个用于结尾的\0。

例如:

c
char str[5]; // 数组长度为5
这个数组可以存储字符串 "abcd",它的内容是:

str[0] = 'a'
str[1] = 'b'
str[2] = 'c'
str[3] = 'd'
str[4] = '\0' // 用于结束字符串

这里,字符串 "abcd" 的长度是4,实际存储的字符是4个,加上结尾的\0,使总的数组长度为5。

  1. 数组长度为5,是否包含\0
    如果你定义一个数组长度为5,并不意味着这个数组会自动包含\0。它只是一个存储空间,数组的内容由程序员管理。

例如:

char arr[5]; // 数组长度为5
在这种情况下,arr的初始内容是未定义的,除非你显式地为数组赋值。例如:

arr[0] = 'h';
arr[1] = 'e';
arr[2] = 'l';
arr[3] = 'l';
arr[4] = '\0';  // 显式地添加'\0'

这里,数组的实际字符是 "hell",后面是\0。如果没有显式地添加\0,那么数组就不是一个有效的字符串。
结论
字符串长度(例如:字符数组定义为 char str[5])意味着可以存储4个字符和1个\0,所以实际有效字符数为4。
数组长度(例如:char arr[5])本身并不自动包含\0,只有当你显式地将其设置为\0时,它才成为一个有效的C字符串。
总之,字符串在C语言中始终以\0结束,而数组则需要程序员自己管理其中的内容和结束标志。
(二)、malloc:
处理复杂数据结构
在实现如链表、图和树等复杂数据结构时,通常需要动态分配节点。这使得可以在运行时根据需要添加或删除节点。

struct Node {
    int data;
    struct Node *next;
};

struct Node *newNode = (struct Node *)malloc(sizeof(struct Node)); // 动态分配节点

反思:这个系统还有许多不足的地方,比如在修改程序的时候,选择修改分数,结果输入成非int型的数据程序就会崩溃,还有当输入超出字符串范围的数据时,系统不会报错,但该数据却不会显示出来。应该加入对输入的数据的类型、大小约束的条件,如果输入错误弹出提醒。

posted @ 2024-10-12 11:26  呓语-MSHK  阅读(58)  评论(0)    收藏  举报