目录
代码总览
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
// 定义学生信息结构体
typedef struct StudentMessage {
char name[20];
int ID;
int accountbalance;
struct StudentMessage* next;
} STU;
// 查询所有账户信息
void SearchAllTheAccounts(STU* phead) {
STU* pb = phead;
while (pb != NULL) {
printf("姓名:%20s || ID账号:%20d\n", pb->name, pb->ID);
pb = pb->next;
}
printf("NULL\n");
}
// 创建账户
void CreateAccount(STU** phead) {
int count = 0;
STU* pb = *phead;
while (pb != NULL) {
count++;
pb = pb->next;
}
pb = *phead;
STU* newp = (STU*)malloc(sizeof(STU)); // 分配新节点的内存
printf("\n输入您的名字:");
scanf("%s", newp->name);
newp->ID = 10001 + count; // 在已有的id序列后添加
printf("\n输入你想充值的金额:");
scanf("%d",&newp->accountbalance);
newp->next = NULL;
while (pb->next != NULL) {
pb = pb->next;
}
pb->next = newp;
printf("您的id号码为%d",newp->ID);
}
void DeleteAccount(STU** phead)//注销账户
{
int count = 0;
STU* pb = *phead;
STU* pf = *phead;
int id;
printf("\n输入您的ID:");
scanf("%d", &id);
while (pb->ID != id && pb->next != NULL)
{
pf = pb;
pb = pb->next;
}
if (pb->ID == id)
{
if (pb == *phead)
{
*phead = pb->next;
}else{
pf->next = pb->next;
}
free(pb);
pb = NULL;
}else{
printf("没有找到您要注销的账户\n");
}
}
void AddAccount(STU* phead)//充值余额
{
int addation;
STU* pb = phead;
int count = 0;
int id;
printf("\n输入您的ID:");
scanf("%d", &id);
printf("\n输入你要充值的金额:");
scanf("%d", &addation);
while (pb != NULL) {
if (pb->ID == id) {
pb->accountbalance += addation;
count++;
break;
}
else {
pb = pb->next;
}
}
if (count == 0) {
printf("\n没有查找到您的信息");
}
}
void SerchAccountbanlance(STU* phead)//查找账户余额
{
STU* pb = phead;
int count = 0;
int id;
printf("\n输入您的ID:");
scanf("%d", &id);
while (pb != NULL) {
if (pb->ID ==id) {
printf("\n您的余额为%d", pb->accountbalance);
count++;
break;
}else{
pb = pb->next;
}
}
if (count == 0) {
printf("\n没有查找到您的信息");
}
}
int main()
{
//创建一个随机数用于给不同学生不同的余额
srand(time(NULL));
// 初始化链表头
STU* head = NULL;
char names[][20] = { "lvzhongyu", "john", "arthon", "majinchao", "jack", "alien", "david", "lucy", "kevin", "adam","xiaohu", "weiwei", "jiejie", "knight", "mlxg", "theshy", "faker", "zues", "guma", "droan" };
for (int i = 0; i < 20; ++i) {
STU* newStudent = (STU*)malloc(sizeof(STU));
// 设置每个学生的信息
snprintf(newStudent->name, sizeof(newStudent->name), "%s", names[i]);
newStudent->ID = 10001 + i; // 起始ID为10001
newStudent->accountbalance = rand()%201; //初始余额为200元中的任意数
newStudent->next = head; // 在链表头插入
head = newStudent; // 更新链表头
}
char c = ' ';
printf("输入下列几个选项来表示您需要的操作\n"
"A.查询现有的账户ID\n"
"B.查询账户余额\n"
"C.对余额进行充值\n"
"D.创建新的账户\n"
"E.注销您的账户\n");
while (c != '#') {
printf("\n输入您要进行的操作(当您输入#时会结束程序): ");
scanf_s(" %c", &c);
switch (c) {
case 'A':
SearchAllTheAccounts(head);
break;
case 'B':
SerchAccountbanlance(head);
break;
case 'C':
AddAccount(head);
break;
case 'D':
CreateAccount(&head);
break;
case 'E':
DeleteAccount(&head);
break;
}
}
printf("\n感谢您的使用");
STU* cur = head;
while (cur != NULL) {
STU* temp = cur;
cur = cur->next;
free(temp);
}
return 0;
}
定义学生信息结构体
typedef struct StudentMessage {
char name[20];
int ID;
int accountbalance;
struct StudentMessage* next;
} STU;
使用typedef
的作用是使得你可以使用STU
作为struct StudentMessage
的别名。这样,在定义变量或使用结构体类型时,可以使用STU
而不是完整的struct StudentMessage
。这有助于简化代码,提高可读性,以及使得代码更加灵活,因为可以随时修改struct StudentMessage
的定义而不需要修改所有使用到该结构体的地方。
查询所有账户信息的void函数
void SearchAllTheAccounts(STU* phead) {
STU* pb = phead;
while (pb != NULL) {
printf("姓名:%20s || ID账号:%20d\n", pb->name, pb->ID);
pb = pb->next;
}
printf("NULL\n");
}
首先定义一个SearchAllTheAccounts(STU* phead)。其中phead传递的是当前所有账户构成的链表。在函数内,定义一个pb链表,将其赋值为传入的phead链表。随后使用while函数打印出所有账户的信息(条件为pb!=NULL即意为循环遍历一直直到最后一个节点)。printf("NULL\n");
语句的目的是在遍历完链表后输出一个提示信息,表示链表已经结束。这是一种常见的做法,特别是当你使用循环遍历链表时,用于指示当前节点为空(即NULL
),因此已经到达链表的末尾。
查询账户余额的void函数
void SerchAccountbanlance(STU* phead)//查找账户余额
{
STU* pb = phead;
int count = 0;
int id;
printf("\n输入您的ID:");
scanf("%d", &id);
while (pb != NULL) {
if (pb->ID ==id) {
printf("\n您的余额为%d", pb->accountbalance);
count++;
break;
}else{
pb = pb->next;
}
}
if (count == 0) {
printf("\n没有查找到您的信息");
}
}
定义部分与上面的内容同理。这里需要定义一个计数器count,并且要将其初始化为0。随后定义个一将有用户输入的id账户,用于检查他是否与现有链表中的id相符(这里的检查办法是使用一个计数器,当有相等的时候对计数器count进行自增)。随后利用循环进行查找是否库中存在这样一个id,当查找到时,break跳出循环,防止循环无法停止。
对余额进行充值
void AddAccount(STU* phead)//充值余额
{
int addation;
STU* pb = phead;
int count = 0;
int id;
printf("\n输入您的ID:");
scanf("%d", &id);
printf("\n输入你要充值的金额:");
scanf("%d", &addation);
while (pb != NULL) {
if (pb->ID == id) {
pb->accountbalance += addation;
count++;
break;
}
else {
pb = pb->next;
}
}
if (count == 0) {
printf("\n没有查找到您的信息");
}
}
函数定义部分依旧与上面的类似。这部分函数大部分与上面的函数相同,只是需要用户输入一个他想充值的金额。并且在原有的基础上加上这么一个数字即可。
创建一个账户
void CreateAccount(STU** phead) {
int count = 0;
STU* pb = *phead;
while (pb != NULL) {
count++;
pb = pb->next;
}
pb = *phead;
STU* newp = (STU*)malloc(sizeof(STU)); // 分配新节点的内存
printf("\n输入您的名字:");
scanf("%s", newp->name);
newp->ID = 10001 + count; // 在已有的id序列后添加
printf("\n输入你想充值的金额:");
scanf("%d",&newp->accountbalance);
newp->next = NULL;
while (pb->next != NULL) {
pb = pb->next;
}
pb->next = newp;
printf("您的id号码为%d",newp->ID);
}
传递STU** phead
的参数通常用于在函数内部修改调用者传递的指针的值。这是因为C语言中的函数参数传递是按值传递的,这意味着函数内部对参数的修改不会影响到调用者。当你传递一个指针给函数时,函数获得了指针的拷贝,因此它可以修改指针所指向的内容,但不能修改指针本身。如果你想在函数内部修改指针本身,你需要传递指针的指针,即STU** phead
。
同样的,我们需要一个计数器,但是功能与上一个不相同,这个count是用于查看当前列表中有多少人,这样可以在新创办一个用户时按照既定好的顺序安排id卡号。值得注意的是,当我们遍历完这个链表中的所有部分后,还需要重新将pb赋值为phead。
之后我们正式开始加入一个新的账户。使用STU* newp = (STU*)malloc(sizeof(STU));
-
malloc(sizeof(STU))
:sizeof(STU)
返回STU
类型的大小(即结构体的大小),malloc
函数用于动态分配内存。这段代码请求分配足够的内存,以存储一个STU
类型的结构体。 -
(STU*)
:malloc
返回一个void*
类型的指针,因为它不知道分配的内存将被用于什么类型的数据。在这里,我们将void*
强制类型转换为STU*
,以便将其赋给newp
,这样我们就能够通过newp
指针来操作这段分配的内存。
整个过程的目的是在堆内存中创建一个新的STU
类型的节点,该节点包含了一个名字、ID、账户余额以及一个指向下一个节点的指针。这个新节点的地址被存储在newp
指针中,以便后续的操作可以使用这个节点。
最后就是由用户输入ta的姓名以及想往账户里面存储的款项。最后的最后就是将链表遍历至当前的的后一项,并且把最后一项赋值为我们加入的新的节点。
注销账户
void DeleteAccount(STU** phead)//注销账户
{
STU* pb = *phead;
STU* pf = *phead;
int id;
printf("\n输入您的ID:");
scanf("%d", &id);
while (pb->ID != id && pb->next != NULL)
{
pf = pb;
pb = pb->next;
}
if (pb->ID == id)
{
if (pb == *phead)
{
*phead = pb->next;
}else{
pf->next = pb->next;
}
free(pb);
pb = NULL;
}else{
printf("没有找到您要注销的账户\n");
}
}
这次采用的方法时快慢指针类似方法,定义两个链表,分别都赋值为phead。
首先要做的是遍历pb,在遍历pb的同时将pf赋值为pb的上一个节点。这样可以保证能够同时处理对当前节点和下一个节点的修改。
而后找到我们要注销的账户后,进行分情况讨论(第一个就是我们要删除的或者节点不在第一个)。当第一个就是目标节点是我们直接把phead的地址改为pb的下一个。当不是第一个的情况下,我们直接将pt->next改为pb->next(相当于直接跳过了pb这个我们要删除的账户)。一定要记得释放我们删去的这个节点对应的内存。
主函数部分
int main()
{
//创建一个随机数用于给不同学生不同的余额
srand(time(NULL));
// 初始化链表头
STU* head = NULL;
char names[][20] = { "lvzhongyu", "john", "arthon", "majinchao", "jack", "alien", "david", "lucy", "kevin", "adam","xiaohu", "weiwei", "jiejie", "knight", "mlxg", "theshy", "faker", "zues", "guma", "droan" };
for (int i = 0; i < 20; ++i) {
STU* newStudent = (STU*)malloc(sizeof(STU));
// 设置每个学生的信息
snprintf(newStudent->name, sizeof(newStudent->name), "%s", names[i]);
newStudent->ID = 10001 + i; // 起始ID为10001
newStudent->accountbalance = rand()%201; //初始余额为200元中的任意数
newStudent->next = head; // 在链表头插入
head = newStudent; // 更新链表头
}
char c = ' ';
printf("输入下列几个选项来表示您需要的操作\n"
"A.查询现有的账户ID\n"
"B.查询账户余额\n"
"C.对余额进行充值\n"
"D.创建新的账户\n"
"E.注销您的账户\n");
while (c != '#') {
printf("\n输入您要进行的操作(当您输入#时会结束程序): ");
scanf_s(" %c", &c);
switch (c) {
case 'A':
SearchAllTheAccounts(head);
break;
case 'B':
SerchAccountbanlance(head);
break;
case 'C':
AddAccount(head);
break;
case 'D':
CreateAccount(&head);
break;
case 'E':
DeleteAccount(&head);
break;
}
}
printf("\n感谢您的使用");
STU* cur = head;
while (cur != NULL) {
STU* temp = cur;
cur = cur->next;
free(temp);
}
return 0;
}
因为我是预先留存了20个人的信息于这个head链表中。我们首先定义一个二维数组用于存储这20个人的名字,之后利用循环,逐个建立节点并且使用snprintf(newStudent->name, sizeof(newStudent->name), "%s", names[i]);将二维数组中的名字挨个赋给它们。同时利用随机数函数随机给这20个人分配账户余额。之后还需要更新链表头,随后就是询问用户需要的操作。在完成所有程序之后。我们需要释放链表中所有的节点即
STU* cur = head;
while (cur != NULL) {
STU* temp = cur;
cur = cur->next;
free(temp);
}