懒猫老师-C语言-链表作业1:学生管理系统(代码模板) - 知乎
懒猫老师链表作业实现–学生信息管理系统 - 知乎
1 模板框架
#include <stdio.h> #include<string.h> #include<malloc.h> #include<stdlib.h> #include<stdbool.h>
#define NO_LENGTH 20 #define NAME_LENGTH 11
typedef struct Student{ char studentNo[NO_LENGTH]; char studentName[NAME_LENGTH]; }st;
typedef struct node { struct Student data; struct node *next; }Node,*Link;
void myMenu(){ printf(" * * * * * * * * * 菜 单 * * * * * * * * * *\n"); printf(" 1 增加学生记录 2 删除学生记录 \n"); printf(" 3 查找学生记录 4 修改学生记录 \n"); printf(" 5 统计学生人数 6 显示学生记录 \n"); printf(" 7 退出系统 \n"); printf(" * * * * * * * * * * * * * * * * * * * * * * * *\n"); }
void inputStudent(Link l){ printf("请输入学生学号:"); scanf("%s",l->data.studentNo); printf("请输入学生的姓名:"); scanf("%s",l->data.studentName);
l->next = NULL; }
void inputStudentNo(char s[],char no[]){ printf("请输入要%s的学生学号:",s); scanf("%s",no); }
void displayNode(Link head){ }
bool addNode(Link head){ Link p,q; Link node; node=(Link)malloc(sizeof(Node)); inputStudent(node);
q = head; p = head->next; if(head->next==NULL) head->next = node; else { while(p != NULL){ if (node->data.studentNo < p->data.studentNo){ q->next = node; node->next = p; return true; } else{ q = p; p = p->next;
} } q->next = node;
} return true; }
bool deleteNode(Link head){
char no[NO_LENGTH]; inputStudentNo("修改",no);
return false; }
bool queryNode(Link head){
char no[NO_LENGTH]; inputStudentNo("修改",no);
return false; }
bool modifyNode(Link head){
char no[NO_LENGTH]; inputStudentNo("修改",no);
return false; }
int countNode(Link head){ Link p; int count = 0; p = head->next;
return false; }
void clearLink(Link head){ Link q,p; }
int main() { int select; int count; Link head;
head = (Link)malloc(sizeof(Node)); head->next = NULL;
while(1) { myMenu(); printf("\n请输入你的选择(0-7):"); scanf("%d",&select); switch(select) { case 1: if(addNode(head)) printf("成功插入一个学生记录。\n\n"); break; case 2: if(deleteNode(head)) printf("成功删除一个学生记录。\n\n"); else printf("没有找到要删除的学生节点。\n\n"); break; case 3: if(queryNode(head)) printf("成功找到学生记录。\n\n"); else printf("没有找到要查询的学生节点。\n\n"); break; case 4: if(modifyNode(head)) printf("成功修改一个学生记录。\n\n"); else printf("没有找到要修改的学生节点。\n\n"); break; case 5: count = countNode(head); printf("学生人数为:%d\n\n",count); break; case 6: displayNode(head); break; case 7: clearLink(head); return 0; default: printf("输入不正确,应该输入0-7之间的数。\n\n"); break; } } return 0; }
|
2 代码
这也太长了……感觉可以优化……
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h>
#define NO_LENGTH 20 #define NAME_LENGTH 11
typedef struct Student { char studentNo[NO_LENGTH]; char studentName[NAME_LENGTH]; } Student;
typedef struct Node { Student data; struct Node *next; } Node, *Link;
void myMenu(); void inputStudent(Student *student); void inputStudentNo(const char* s, char no[]); void displayNode(Link head); bool addNode(Link head); bool deleteNode(Link head); bool queryNode(Link head); bool modifyNode(Link head); int countNode(Link head); void clearLink(Link head);
int main(void) { Link head = (Link)malloc(sizeof(Node)); head->next = NULL; int select; while (true) { myMenu(); printf("请输入你的选择(1-7): "); scanf("%d", &select); switch (select) { case 1: if (addNode(head)) printf("成功插入一个学生记录.\n\n"); break; case 2: if (deleteNode(head)) printf("成功删除一个学生记录.\n\n"); else printf("没有找到要删除的学生节点.\n\n"); break; case 3: if (queryNode(head)) printf("成功找到学生记录.\n\n"); else printf("没有找到要查询的学生节点.\n\n"); break; case 4: if (modifyNode(head)) printf("成功修改一个学生记录.\n\n"); else printf("没有找到要修改的学生节点.\n\n"); break; case 5: printf("学生人数为: %d\n\n", countNode(head)); break; case 6: displayNode(head); break; case 7: clearLink(head); return 0; default: printf("输入不正确, 应该输入1-7之间的数.\n\n"); break; } } return 0; }
void myMenu() { printf(" * * * * * * * * * * 菜 单 * * * * * * * * * \n"); printf(" 1 增加学生记录 2 删除学生记录 \n"); printf(" 3 查找学生记录 4 修改学生记录 \n"); printf(" 5 统计学生记录 6 显示学生记录 \n"); printf(" 7 退出系统 \n"); printf("* * * * * * * * * * * * * * * * * * * * * * * \n\n"); }
void inputStudent(Student *student) { printf("请输入学生学号: "); scanf("%s", student->studentNo); printf("请输入学生的姓名: "); scanf("%s", student->studentName); }
void inputStudentNo(const char* s, char no[]) { printf("请输入要%s的学生学号: ", s); scanf("%s", no); }
void displayNode(Link head) { Link p = head; if (p == NULL || p->next == NULL){ printf("暂无学生信息录入!\n"); return; } while (p->next != NULL) { printf("%s %s\n", p->data.studentNo, p->data.studentName); p = p->next; } }
bool addNode(Link head) { if (head == NULL) { printf("链表不存在!\n"); return false; } Link p, q, node; node = (Link)malloc(sizeof(Node)); inputStudent(&node->data); q = head; p = head->next; if (head->next == NULL) head->next = node; else { while (p != NULL) { if (strcmp(node->data.studentNo, p->data.studentNo) < 0) { q->next = node; node->next = p; return true; } else { q = p; p = p->next; } } q->next = node; } return true; }
bool deleteNode(Link head) { if (head == NULL || head->next == NULL) { printf("暂无学生信息!\n"); return false; } char no[NO_LENGTH]; inputStudentNo("删除", no); Link p, q; q = head; p = head->next; while (p != NULL) { if (strcmp(p->data.studentNo, no) == 0) { q->next = p->next; free(p); return true; } else { q = p; p = p->next; } } return false; }
bool queryNode(Link head) { if (head == NULL || head->next == NULL) { printf("暂无学生录入!\n"); return false; } char no[NO_LENGTH]; inputStudentNo("查询", no); for (Link p = head->next; p != NULL; p = p->next) { if (strcmp(p->data.studentNo, no) == 0) { printf("查询成功!\n"); printf("此学生学号为: %s\n", p->data.studentNo); printf("此学生姓名为: %s\n", p->data.studentName); return true; } } printf("没有找到该学生!\n"); return false; }
bool modifyNode(Link head) { if (head == NULL || head->next == NULL) { printf("暂无学生录入!\n"); return false; } char no[NO_LENGTH]; inputStudentNo("修改", no); for (Link p = head->next; p != NULL; p = p->next) { if (strcmp(p->data.studentNo, no) == 0) { printf("您要修改的学生记录如下, 请确认:\n"); printf("学号: %s\n", p->data.studentNo); printf("姓名: %s\n", p->data.studentName); printf("请选择修改 (1: 学号; 2: 姓名)\n"); int x; scanf("%d", &x); switch (x) { case 1: printf("请输入要修改的学号:\n"); scanf("%s", p->data.studentNo); break; case 2: printf("请输入要修改的姓名:\n"); scanf("%s", p->data.studentName); break; } return true; } } return false; }
int countNode(Link head) { if (head == NULL || head->next == NULL) { printf("暂无学生信息!\n"); return 0; } int count = 0; for (Link p = head->next; p != NULL; p = p->next) { count++; } return count; }
void clearLink(Link head) { if (head == NULL || head->next == NULL) { printf("暂无学生信息!\n"); return; } Link p, q; q = head; p = head->next; while (p != NULL) { q = p->next; free(p); p = q; } free(head); printf("清除成功!\n"); }
|
3 遇到的问题
3.1 displayNode( ) 输出一行乱码
void displayNode(Link head){ Link p = head; if (p == NULL || p->next == NULL){ printf("暂无学生信息录入!\n\n"); } while (p != NULL){ printf("%s %s\n", p->data.studentNo, p->data.studentName); p = p->next; } }
|
问题出在 while() 中的循环条件和判断条件上, 当循环到最后一个结点时, p 的 next 一定为空, 即 p->next == NULL, 然后循环继续执行一次, 会多输出一行.
改正: while (p->next != NULL), 这样循环会在打印最后一个节点之后终止, 而不会再多输出一行乱码. 修改后的代码如下所示:
while (p->next != NULL) { ... }
|
3.2 bool deleteNode( ) 字符串常量问题
把文件从 c 转 c++ 时:
void inputStudentNo(char s[], char no[]){ printf("请输入要%s的学生学号", s); scanf("%s", no); }
|
出现错误:
In function 'bool deleteNode(Link)': [警告] ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
|
代码中将字符串常量赋值给 char* 类型的变量, 这在 C++ 中是不允许的, 因为字符串常量是不可变的, 而将其赋值给 char* 类型的变量可能会导致试图修改常量字符串的行为, 这是不安全的。
所以, 可以将字符数组声明为常量指针, 即使用 const char* 类型而不是 char* 类型。这样做可以避免试图修改字符串常量, 并消除警告
例如,将 inputStudentNo() 函数中的参数 s 改为 const char* 类型:
void inputStudentNo(const char* s, char no[]){ printf("请输入要%s的学生学号", s); scanf("%s", no); }
|
详细解释:
C++对于字符串常量的处理有所不同,它们是 const char* 类型而不是 char* 类型。因此,在C++中,需要将函数参数声明为 const char* 而不是 char[] 类型。
在C++中,字符串常量(如 “删除”)被视为字符数组的地址,类型为 **const char***,而不是 char*。这是因为字符串常量是不可变的,所以C++将它们声明为常量,不允许对它们进行修改。因此,在C++中,需要使用 const char* 来接收字符串常量的地址.
3.3 char* no 和 char no[]的区别
在函数参数中,char* no 和 char no[] 都可以用来接收字符串的地址。在这两种情况下,传递给函数的实际参数都将被视为字符串的起始地址。
然而,它们之间确实存在一些微妙的差别:
数组长度:
- char* no 中的 no 是一个指向字符的指针,它只包含了字符串的起始地址,并没有包含任何有关字符串长度的信息。在这种情况下,函数无法直接知道传递的字符串有多长,除非在字符串中使用了特殊的结束符(如空字符 \0)。
- char no[] 中的 no 是一个字符数组,它实际上包含了整个字符串的内容。编译器会自动为数组分配足够的空间来存储字符串,并且可以通过 sizeof 运算符获取数组的长度(不包括空字符 \0)。
参数传递:
- 在函数调用时,传递给 char* no 的参数可以是一个字符数组的地址,也可以是指向字符串常量的指针,甚至可以是一个动态分配的字符数组。这样的参数传递方式更加灵活。
- 而对于 **char no[]**,函数参数必须是一个数组,因此只能传递一个字符数组。
总的来说,char* no 更常用于函数参数,因为它更加灵活,可以接收多种类型的字符串参数。而 char no[] 在声明函数参数时,更适合于限定参数为一个字符数组的情况。