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(空字符)的方式是非常重要的。以下是对你问题的详细解答:
- 字符串长度为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。
- 数组长度为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型的数据程序就会崩溃,还有当输入超出字符串范围的数据时,系统不会报错,但该数据却不会显示出来。应该加入对输入的数据的类型、大小约束的条件,如果输入错误弹出提醒。
标签:head,struct,temp,管理系统,链表,num,printf,NULL,语言 From: https://www.cnblogs.com/LiuHuWei/p/18460176