#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//图书信息
struct bookInfo
{
char name[20];//书名
char writter[100];//作者
char publisher[100];//出版社
int price; //价格
int num; //数量
char introduction[100];//简介
};
struct Node //Node节点
{
struct bookInfo data; //链表里存的struct bookInfo类型
struct Node* next;
};
struct Node* list = NULL; //更改为全局的列表为空,然后在主函数那初始化
//创建表头:表头就是一个结构体变量
struct Node* createHead()
{
/*动态内存申请
sizeof(struct node)就是求 struct node 这个结构体占用的字节数。
malloc(sizeof(struct node))申请 struct node 这个结构体占用字节数大小的空间
(struct node *) malloc(sizeof(struct node))将申请的空间的地址强制转化为 struct node * 指针类型
headNade=(struct node *) malloc(sizeof(struct node))将那个强制转化的地址赋值给 headNade.
*/
struct Node* headNode=(struct Node*)malloc(sizeof(struct Node));
//变量的基本规则:使用前必须初始化,在这里只需要给next指针初始化,表头不需要存储数据,所以不用初始化
headNode->next=NULL; //表头不存数据
return headNode; //返回创建链表的头指针
};
//创建节点,为插入做准备
//用户的数据变为结构体的变量
struct Node* createNode(struct bookInfo data)
{
struct Node* newNode=(struct Node*)malloc(sizeof(struct Node));
newNode->data=data;
newNode->next=NULL;
return newNode;
};
//插入,表头法插入
void insertNodeByHead(struct Node* headNode,struct bookInfo data)
{
struct Node* newNode=createNode(data); //通过调用子函数,创建结点
//必须先连接,后断开
newNode->next=headNode->next; //头插法,headnode->next为下一个结点(表头下一个结点)的地址,newnode的指针域指向下一个结点(地址)
headNode->next=newNode; // 原表头指向新建的结点(地址)
}
//指定位置删除
//posLeftNode->next=posNode->next;相邻指针
//free(posNode);指定删除的代码,删除完后要把posNode=NULL
void deleteNodeByName(struct Node* headNode,char *bookName) //通过书籍名字进行删除
{
/*需要两个指针进行删除,先删除节点指针的Left左边,其次再删除节点指针的右边,一前一后*/
struct Node* posLeftNode=headNode;
struct Node* posNode=headNode->next;
/*书籍名字是字符串,所以要使用字符串比较函数处理
当我们删除节点不等于空,
并且使用strcmp()比较,
使用posNode->data.name和我们传过来的参数bookName进行比较*/
while(posNode!=NULL&&strcmp(posNode->data.name,bookName))
{
/*让节点并排往下走*/
posLeftNode=posNode;
posNode=posLeftNode->next;
}
//讨论查找结果,如果posNode为空,那就是没有找到,无法删除,找到就删除
if(posNode==NULL)
return;
else
{
printf("删除成功!\n");
posLeftNode->next=posNode->next;
free(posNode);
posNode=NULL;
}
}
//添加链表的查找函数,把找到的节点给它返回
struct Node* searchByName(struct Node* headNode,char *bookName)
{
//只需要找到那个节点即可,不需要两个指针
struct Node* posNode = headNode->next;
while(posNode!=NULL&&strcmp(posNode->data.name,bookName)) //strcmp()比较括号里面的,如果相等,就退出循环,如果不等,那就执行循环
{
posNode=posNode->next;
}
return posNode;
}
//打印链表
void printList(struct Node* headNode)
{
/*定义一个移动的指针pMove从第二个节点headNode->next开始打印*/
struct Node* pMove=headNode->next;
printf("书名\t作者\t出版社\t价格\t数量\t简介\n"); //表头
while(pMove != NULL) // pMove不等于空的时候,打印节点的数据
{
//剥洋葱,一层一层
printf("%s\t%s\t%s\t%d\t%d\t%s\n",pMove->data.name,pMove->data.writter,pMove->data.publisher,pMove->data.price,pMove->data.num,pMove->data.introduction); //让pMove往下一个节点走
pMove=pMove->next;
}
}
void makeMenu()//界面菜单
{
printf("欢迎使用图书管理信息系统\n");
printf("1.登记书籍信息\n");
printf("2.浏览书籍查询\n");
printf("3.借阅书籍\n");
printf("4.归还书籍\n");
printf("5.删除书籍\n");
printf("6.查找书籍\n");
printf("0.退出系统\n\n");
printf("请选择您想使用的功能0-8:");
}
//文件存操作
void saveInfoToFile(const char *fileName,struct Node* headNode) //将链表里的信息打印到文件里去
{
FILE *fp=fopen(fileName,"w"); //第一个指针使用写的方式打开文件
struct Node* pMove=headNode->next;
//当第二个节点不等于空时,就将链表里的信息打印到文件里去
while(pMove)
{
fprintf(fp,"%s\t%s\t%s\t%d\t%d\t%s\n",pMove->data.name,pMove->data.writter,pMove->data.publisher,pMove->data.price,pMove->data.num,pMove->data.introduction); //打印到指针文件指向的文件,打印的是pMove
pMove=pMove->next;
}
fclose(fp); //关闭文件
}
//文件读操作
void readInfoFromFile(const char* fileName,struct Node* headNode) //需要从文件里将内容读出
{
//把文件当作是一个输入键盘,定义临时变量tempData,把数据读到变量(如:%s)里
struct bookInfo tempData;
FILE* fp=fopen(fileName,"r"); //第一个指针使用读的方式打开文件
/*文件开始的时候是不存在的,不存在的时候,使用创建(w+)的读写方式打开文件*/
if(fp==NULL)
{
fp=fopen(fileName,"w+");
}
while(fscanf(fp,"%s\t%s\t%s\t%d\t%d\t%s\n",pMove->data.name,pMove->data.writter,pMove->data.publisher,pMove->data.price,pMove->data.num,pMove->data.introduction)!=EOF) //加上" != EOF"后该程序就不是死循环了
{
/*做链表的插入,读一个信息就插入到链表中*/
insertNodeByHead(list,tempData);
}
fclose(fp); //关闭文件
}
//按键处理,做交互
void keyDown()
{
int userKey=0;
//准备一个临时变量去存放书籍的临时信息
struct bookInfo tempBook;
//写个临时的指针用于查询
struct Node* result=NULL;
scanf("%d",&userKey);
switch (userKey)
{
case 0:
printf("\n-----退出-----\n");
printf("退出成功\n");
system("pause"); //防止闪屏
exit(0); //关闭掉整个程序
break;
case 1:
printf("\n-----登记-----\n");
printf("书名:");
scanf("%s",tempBook.name);
printf("作者:");
scanf("%s",tempBook.writter);
printf("出版社:");
scanf("%s",tempBook.publisher);
printf("价格/元:");
scanf("%d",&tempBook.price);
printf("数量:");
scanf("%d",&tempBook.num);
printf("简介(<100字):");
scanf("%s",tempBook.introduction);
//scanf("%s%f%d",tempBook.name,&tempBook.price,&tempBook.num); //tempBook.name不需要加&取地址,因为是字符串
insertNodeByHead(list, tempBook); // 插入链表
saveInfoToFile("bookinfo.txt",list); //当做了数据修改,就将信息保存到bookinfo.txt文件中,读到链表中
break;
case 2:
printf("\n-----浏览-----\n");
printList(list); //打印链表
break;
case 3:
printf("\n-----借阅-----\n"); //是把书的数量减少,书籍存在可以借,书的数量-1,不存在则提示借书失败
//通过书籍名去借阅
printf("请输入需要借阅的书名:");
scanf("%s",tempBook.name);
result=searchByName(list,tempBook.name);
if(result==NULL)
{
printf("没有此书籍,无法借阅!");
}
else
{
if(result->data.num>0) //大于0说明有书籍可借阅
{
result->data.num--; //书的数量-1
printf("借阅成功");
}
else
{
printf("当前书籍无库存,借阅失败");
}
}
break;
case 4:
printf("\n-----归还-----\n"); //是把当前书的数量增加+1
printf("请输入需要归还的书名:");
scanf("%s",tempBook.name);
result=searchByName(list,tempBook.name);
if(result==NULL)
{
printf("没有此书籍借阅记录,无法归还");
}
else
{
result->data.num++; //书的数量+1
printf("归还成功");
}
break;
case 5:
printf("\n-----删除-----\n");
printf("请输入要删除书籍的名字:");
scanf("%s",tempBook.name);
deleteNodeByName(list,tempBook.name);
saveInfoToFile("bookinfo.txt",list); //删除之后需要同步到文件,因为涉及到数据的修改
break;
case 6:
printf("\n-----查找-----\n");
printf("请输入要查询书籍的名字:");
scanf("%s",tempBook.name);
result=searchByName(list,tempBook.name);
//查找等效于链表的查找
//判断如果result等于NULL,就表示未找到
if(result==NULL)
{
printf("未找到该书籍");
}
else
{
printf("书名\t价格\t数量\n");
printf("%s\t%s\t%s\t%d\t%d\t%s\n",pMove->data.name,pMove->data.writter,pMove->data.publisher,pMove->data.price,pMove->data.num,pMove->data.introduction);
}
break;
default:
printf("\n-----错误-----\n"); //在switch语句中,如果表达式的值与每一中情况都不同,则执行default:后面的语句
break;
}
}
//调用测试实现
int main()
{
list=createHead();
readInfoFromFile("bookinfo.txt",list); //当系统运行的时候,就将文件的东西读到列表里
/*while(1)其中1代表一个常量表达式,
它永远不会等于0。循环会一直执行下去。
除非你设置break等类似的跳出循环语句循环才会中止。*/
while(1)
{
makeMenu();
keyDown();
system("pause");
system("cls"); //清屏代码:
}
system("pause"); //就是暂停程序的执行,等待任意健继续执行。
return 0;
}
这报错不是已经很清楚了?pMove未定义,你上面的其他函数用pMove都有定义的。
pMove未定义,使用前需要先定义
修改完善如下,供参考:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//图书信息
struct bookInfo
{
char name[20];//书名
char writter[100];//作者
char publisher[100];//出版社
int price; //价格
int num; //数量
char introduction[100];//简介
};
struct Node //Node节点
{
struct bookInfo data; //链表里存的struct bookInfo类型
struct Node* next;
};
struct Node* list = NULL; //更改为全局的列表为空,然后在主函数那初始化
//创建表头:表头就是一个结构体变量
struct Node* createHead()
{
/*动态内存申请
sizeof(struct node)就是求 struct node 这个结构体占用的字节数。
malloc(sizeof(struct node))申请 struct node 这个结构体占用字节数大小的空间
(struct node *) malloc(sizeof(struct node))将申请的空间的地址强制转化为 struct node * 指针类型
headNade=(struct node *) malloc(sizeof(struct node))将那个强制转化的地址赋值给 headNade.
*/
struct Node* headNode=(struct Node*)malloc(sizeof(struct Node));
//变量的基本规则:使用前必须初始化,在这里只需要给next指针初始化,表头不需要存储数据,所以不用初始化
headNode->next=NULL; //表头不存数据
return headNode; //返回创建链表的头指针
};
//创建节点,为插入做准备
//用户的数据变为结构体的变量
struct Node* createNode(struct bookInfo data)
{
struct Node* newNode=(struct Node*)malloc(sizeof(struct Node));
newNode->data=data;
newNode->next=NULL;
return newNode;
};
//插入,表头法插入
void insertNodeByHead(struct Node* headNode,struct bookInfo data)
{
struct Node* newNode=createNode(data); //通过调用子函数,创建结点
//必须先连接,后断开
newNode->next=headNode->next;
//头插法,headnode->next为下一个结点(表头下一个结点)的地址,newnode的指针域指向下一个结点(地址)
headNode->next=newNode;
// 原表头指向新建的结点(地址)
}
//指定位置删除
//posLeftNode->next=posNode->next;相邻指针
//free(posNode);指定删除的代码,删除完后要把posNode=NULL
void deleteNodeByName(struct Node* headNode,char *bookName) //通过书籍名字进行删除
{
/*需要两个指针进行删除,先删除节点指针的Left左边,其次再删除节点指针的右边,一前一后*/
struct Node* posLeftNode=headNode;
struct Node* posNode=headNode->next;
/*书籍名字是字符串,所以要使用字符串比较函数处理
当我们删除节点不等于空,
并且使用strcmp()比较,
使用posNode->data.name和我们传过来的参数bookName进行比较*/
while(posNode!=NULL&&strcmp(posNode->data.name,bookName))
{
/*让节点并排往下走*/
posLeftNode=posNode;
posNode=posLeftNode->next;
}
//讨论查找结果,如果posNode为空,那就是没有找到,无法删除,找到就删除
if(posNode==NULL)
return;
else
{
printf("删除成功!\n");
posLeftNode->next=posNode->next;
free(posNode);
posNode=NULL;
}
}
//添加链表的查找函数,把找到的节点给它返回
struct Node* searchByName(struct Node* headNode,char *bookName)
{
//只需要找到那个节点即可,不需要两个指针
struct Node* posNode = headNode->next;
while(posNode!=NULL && strcmp(posNode->data.name,bookName))
//strcmp()比较括号里面的,如果相等,就退出循环,如果不等,那就执行循环
{
posNode=posNode->next;
}
return posNode;
}
//打印链表
void printList(struct Node* headNode)
{
/*定义一个移动的指针pMove从第二个节点headNode->next开始打印*/
struct Node* pMove=headNode->next;
printf("书名\t作者\t出版社\t价格\t数量\t简介\n"); //表头
while(pMove != NULL) // pMove不等于空的时候,打印节点的数据
{
//剥洋葱,一层一层
printf("%s\t%s\t%s\t%d\t%d\t%s\n",pMove->data.name,pMove->data.writter,
pMove->data.publisher,pMove->data.price,pMove->data.num,pMove->data.introduction);
//让pMove往下一个节点走
pMove=pMove->next;
}
}
void makeMenu()//界面菜单
{
printf("欢迎使用图书管理信息系统\n");
printf("1.登记书籍信息\n");
printf("2.浏览书籍查询\n");
printf("3.借阅书籍\n");
printf("4.归还书籍\n");
printf("5.删除书籍\n");
printf("6.查找书籍\n");
printf("0.退出系统\n\n");
printf("请选择您想使用的功能0-8:");
}
//文件存操作
void saveInfoToFile(const char *fileName,struct Node* headNode) //将链表里的信息打印到文件里去
{
FILE *fp=fopen(fileName,"w"); //第一个指针使用写的方式打开文件
struct Node* pMove=headNode->next;
//当第二个节点不等于空时,就将链表里的信息打印到文件里去
while(pMove)
{
fprintf(fp,"%s\t%s\t%s\t%d\t%d\t%s\n",pMove->data.name,pMove->data.writter,
pMove->data.publisher,pMove->data.price,pMove->data.num,pMove->data.introduction); //打印到指针文件指向的文件,打印的是pMove
pMove=pMove->next;
}
fclose(fp); //关闭文件
}
//文件读操作
void readInfoFromFile(const char* fileName,struct Node* headNode) //需要从文件里将内容读出
{
//把文件当作是一个输入键盘,定义临时变量tempData,把数据读到变量(如:%s)里
struct bookInfo tempData;
FILE* fp=fopen(fileName,"r"); //第一个指针使用读的方式打开文件
/*文件开始的时候是不存在的,不存在的时候,使用创建(w+)的读写方式打开文件*/
if(fp==NULL)
{
fp=fopen(fileName,"w+");
}
//while(fscanf(fp,"%s\t%s\t%s\t%d\t%d\t%s\n",pMove->data.name,pMove->data.writter, 修改
// pMove->data.publisher,pMove->data.price,pMove->data.num,pMove->data.introduction)!=EOF) //加上" != EOF"后该程序就不是死循环了
while (1)
{
if(fscanf(fp,"%s\t%s\t%s\t%d\t%d\t%s\n",tempData.name,tempData.writter,
tempData.publisher,&tempData.price,&tempData.num,tempData.introduction) != 6) break;
/*做链表的插入,读一个信息就插入到链表中*/
insertNodeByHead(list,tempData);
}
fclose(fp); //关闭文件
}
//按键处理,做交互
void keyDown()
{
int userKey=0;
//准备一个临时变量去存放书籍的临时信息
struct bookInfo tempBook;
//写个临时的指针用于查询
struct Node* result=NULL;
scanf("%d",&userKey);
switch (userKey)
{
case 0:
printf("\n-----退出-----\n");
printf("退出成功\n");
system("pause"); //防止闪屏
exit(0); //关闭掉整个程序
break;
case 1:
printf("\n-----登记-----\n");
printf("书名:");
scanf("%s",tempBook.name);
getchar();
printf("作者:");
scanf("%s",tempBook.writter);
getchar();
printf("出版社:");
scanf("%s",tempBook.publisher);
getchar();
printf("价格(整数:元):");
scanf("%d",&tempBook.price);
getchar();
printf("数量(整数):");
scanf("%d",&tempBook.num);
getchar();
printf("简介(<100字):");
gets(tempBook.introduction); //scanf("%s",tempBook.introduction); 修改
//scanf("%s%f%d",tempBook.name,&tempBook.price,&tempBook.num);
//tempBook.name不需要加&取地址,因为是字符串
insertNodeByHead(list, tempBook); // 插入链表
saveInfoToFile("bookinfo.txt",list);
//当做了数据修改,就将信息保存到bookinfo.txt文件中,读到链表中
break;
case 2:
printf("\n-----浏览-----\n");
printList(list); //打印链表
break;
case 3:
printf("\n-----借阅-----\n");
//是把书的数量减少,书籍存在可以借,书的数量-1,不存在则提示借书失败
//通过书籍名去借阅
printf("请输入需要借阅的书名:");
scanf("%s",tempBook.name);
getchar();
result=searchByName(list,tempBook.name);
if(result==NULL)
{
printf("没有此书籍,无法借阅!");
}
else
{
if(result->data.num>0) //大于0说明有书籍可借阅
{
result->data.num--; //书的数量-1
printf("借阅成功");
}
else
{
printf("当前书籍无库存,借阅失败");
}
}
break;
case 4:
printf("\n-----归还-----\n"); //是把当前书的数量增加+1
printf("请输入需要归还的书名:");
scanf("%s",tempBook.name);
getchar();
result=searchByName(list,tempBook.name);
if(result==NULL)
{
printf("没有此书籍借阅记录,无法归还\n");
}
else
{
result->data.num++; //书的数量+1
printf("归还成功");
}
break;
case 5:
printf("\n-----删除-----\n");
printf("请输入要删除书籍的名字:");
scanf("%s",tempBook.name);
getchar();
deleteNodeByName(list,tempBook.name);
saveInfoToFile("bookinfo.txt",list);
//删除之后需要同步到文件,因为涉及到数据的修改
break;
case 6:
printf("\n-----查找-----\n");
printf("请输入要查询书籍的名字:");
scanf("%s",tempBook.name);
getchar();
result=searchByName(list,tempBook.name);
//查找等效于链表的查找
//判断如果result等于NULL,就表示未找到
if(result==NULL)
{
printf("未找到该书籍\n");
}
else
{
printf("书名\t作者\t出版社\t价格\t数量\t简介\n");
//printf("%s\t%s\t%s\t%d\t%d\t%s\n",pMove->data.name,pMove->data.writter, 修改
// pMove->data.publisher,pMove->data.price,pMove->data.num,pMove->data.introduction);
printf("%s\t%s\t%s\t%d\t%d\t%s\n",result->data.name,result->data.writter,
result->data.publisher,result->data.price,result->data.num,result->data.introduction);
}
break;
default:
printf("\n-----错误-----\n");
//在switch语句中,如果表达式的值与每一中情况都不同,则执行default:后面的语句
break;
}
}
//调用测试实现
int main()
{
list=createHead();
readInfoFromFile("bookinfo.txt",list); //当系统运行的时候,就将文件的东西读到列表里
//while(1)其中1代表一个常量表达式,
//它永远不会等于0。循环会一直执行下去。
//除非你设置break等类似的跳出循环语句循环才会中止。
while(1)
{
makeMenu();
keyDown();
system("pause");
system("cls"); //清屏代码:
}
system("pause"); //就是暂停程序的执行,等待任意健继续执行。
return 0;
}
readInfoFromFile函数中
//文件读操作
void readInfoFromFile(const char* fileName,struct Node* headNode) //需要从文件里将内容读出
{
//把文件当作是一个输入键盘,定义临时变量tempData,把数据读到变量(如:%s)里
struct bookInfo tempData;
FILE* fp=fopen(fileName,"r"); //第一个指针使用读的方式打开文件
/*文件开始的时候是不存在的,不存在的时候,使用创建(w+)的读写方式打开文件*/
if(fp==NULL)
{
fp=fopen(fileName,"w+");
}
while(fscanf(fp,"%s\t%s\t%s\t%d\t%d\t%s\n",tempData.name,tempData.writter,tempData.publisher,tempData.price,tempData.num,tempData.introduction)!=EOF) //加上" != EOF"后该程序就不是死循环了
{
/*做链表的插入,读一个信息就插入到链表中*/
insertNodeByHead(list,tempData);
}
fclose(fp); //关闭文件
}
keyDown() 函数的case 6:
//按键处理,做交互
void keyDown()
{
int userKey=0;
//准备一个临时变量去存放书籍的临时信息
struct bookInfo tempBook;
//写个临时的指针用于查询
struct Node* result=NULL;
scanf("%d",&userKey);
switch (userKey)
{
case 0:
printf("\n-----退出-----\n");
printf("退出成功\n");
system("pause"); //防止闪屏
exit(0); //关闭掉整个程序
break;
case 1:
printf("\n-----登记-----\n");
printf("书名:");
scanf("%s",tempBook.name);
printf("作者:");
scanf("%s",tempBook.writter);
printf("出版社:");
scanf("%s",tempBook.publisher);
printf("价格/元:");
scanf("%d",&tempBook.price);
printf("数量:");
scanf("%d",&tempBook.num);
printf("简介(<100字):");
scanf("%s",tempBook.introduction);
//scanf("%s%f%d",tempBook.name,&tempBook.price,&tempBook.num); //tempBook.name不需要加&取地址,因为是字符串
insertNodeByHead(list, tempBook); // 插入链表
saveInfoToFile("bookinfo.txt",list); //当做了数据修改,就将信息保存到bookinfo.txt文件中,读到链表中
break;
case 2:
printf("\n-----浏览-----\n");
printList(list); //打印链表
break;
case 3:
printf("\n-----借阅-----\n"); //是把书的数量减少,书籍存在可以借,书的数量-1,不存在则提示借书失败
//通过书籍名去借阅
printf("请输入需要借阅的书名:");
scanf("%s",tempBook.name);
result=searchByName(list,tempBook.name);
if(result==NULL)
{
printf("没有此书籍,无法借阅!");
}
else
{
if(result->data.num>0) //大于0说明有书籍可借阅
{
result->data.num--; //书的数量-1
printf("借阅成功");
}
else
{
printf("当前书籍无库存,借阅失败");
}
}
break;
case 4:
printf("\n-----归还-----\n"); //是把当前书的数量增加+1
printf("请输入需要归还的书名:");
scanf("%s",tempBook.name);
result=searchByName(list,tempBook.name);
if(result==NULL)
{
printf("没有此书籍借阅记录,无法归还");
}
else
{
result->data.num++; //书的数量+1
printf("归还成功");
}
break;
case 5:
printf("\n-----删除-----\n");
printf("请输入要删除书籍的名字:");
scanf("%s",tempBook.name);
deleteNodeByName(list,tempBook.name);
saveInfoToFile("bookinfo.txt",list); //删除之后需要同步到文件,因为涉及到数据的修改
break;
case 6:
printf("\n-----查找-----\n");
printf("请输入要查询书籍的名字:");
scanf("%s",tempBook.name);
result=searchByName(list,tempBook.name);
//查找等效于链表的查找
//判断如果result等于NULL,就表示未找到
if(result==NULL)
{
printf("未找到该书籍");
}
else
{
printf("书名\t价格\t数量\n");
printf("%s\t%s\t%s\t%d\t%d\t%s\n",result->data.name,result->data.writter,result->data.publisher,result->data.price,result->data.num,result->data.introduction);
}
break;
default:
printf("\n-----错误-----\n"); //在switch语句中,如果表达式的值与每一中情况都不同,则执行default:后面的语句
break;
}
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//图书信息
struct bookInfo
{
char name[20];//书名
char writter[100];//作者
char publisher[100];//出版社
int price; //价格
int num; //数量
char introduction[100];//简介
};
struct Node //Node节点
{
struct bookInfo data; //链表里存的struct bookInfo类型
struct Node* next;
};
struct Node* list = NULL; //更改为全局的列表为空,然后在主函数那初始化
//创建表头:表头就是一个结构体变量
struct Node* createHead()
{
/*动态内存申请
sizeof(struct node)就是求 struct node 这个结构体占用的字节数。
malloc(sizeof(struct node))申请 struct node 这个结构体占用字节数大小的空间
(struct node *) malloc(sizeof(struct node))将申请的空间的地址强制转化为 struct node * 指针类型
headNade=(struct node *) malloc(sizeof(struct node))将那个强制转化的地址赋值给 headNade.
*/
struct Node* headNode=(struct Node*)malloc(sizeof(struct Node));
//变量的基本规则:使用前必须初始化,在这里只需要给next指针初始化,表头不需要存储数据,所以不用初始化
headNode->next=NULL; //表头不存数据
return headNode; //返回创建链表的头指针
};
//创建节点,为插入做准备
//用户的数据变为结构体的变量
struct Node* createNode(struct bookInfo data)
{
struct Node* newNode=(struct Node*)malloc(sizeof(struct Node));
newNode->data=data;
newNode->next=NULL;
return newNode;
};
//插入,表头法插入
void insertNodeByHead(struct Node* headNode,struct bookInfo data)
{
struct Node* newNode=createNode(data); //通过调用子函数,创建结点
//必须先连接,后断开
newNode->next=headNode->next; //头插法,headnode->next为下一个结点(表头下一个结点)的地址,newnode的指针域指向下一个结点(地址)
headNode->next=newNode; // 原表头指向新建的结点(地址)
}
//指定位置删除
//posLeftNode->next=posNode->next;相邻指针
//free(posNode);指定删除的代码,删除完后要把posNode=NULL
void deleteNodeByName(struct Node* headNode,char *bookName) //通过书籍名字进行删除
{
/*需要两个指针进行删除,先删除节点指针的Left左边,其次再删除节点指针的右边,一前一后*/
struct Node* posLeftNode=headNode;
struct Node* posNode=headNode->next;
/*书籍名字是字符串,所以要使用字符串比较函数处理
当我们删除节点不等于空,
并且使用strcmp()比较,
使用posNode->data.name和我们传过来的参数bookName进行比较*/
while(posNode!=NULL&&strcmp(posNode->data.name,bookName))
{
/*让节点并排往下走*/
posLeftNode=posNode;
posNode=posLeftNode->next;
}
//讨论查找结果,如果posNode为空,那就是没有找到,无法删除,找到就删除
if(posNode==NULL)
return;
else
{
printf("删除成功!\n");
posLeftNode->next=posNode->next;
free(posNode);
posNode=NULL;
}
}
//添加链表的查找函数,把找到的节点给它返回
struct Node* searchByName(struct Node* headNode,char *bookName)
{
//只需要找到那个节点即可,不需要两个指针
struct Node* posNode = headNode->next;
while(posNode!=NULL&&strcmp(posNode->data.name,bookName)) //strcmp()比较括号里面的,如果相等,就退出循环,如果不等,那就执行循环
{
posNode=posNode->next;
}
return posNode;
}
//打印链表
void printList(struct Node* headNode)
{
/*定义一个移动的指针pMove从第二个节点headNode->next开始打印*/
struct Node* pMove=headNode->next;
printf("书名\t作者\t出版社\t价格\t数量\t简介\n"); //表头
while(pMove != NULL) // pMove不等于空的时候,打印节点的数据
{
//剥洋葱,一层一层
printf("%s\t%s\t%s\t%d\t%d\t%s\n",pMove->data.name,pMove->data.writter,pMove->data.publisher,pMove->data.price,pMove->data.num,pMove->data.introduction); //让pMove往下一个节点走
pMove=pMove->next;
}
}
void makeMenu()//界面菜单
{
printf("欢迎使用图书管理信息系统\n");
printf("1.登记书籍信息\n");
printf("2.浏览书籍查询\n");
printf("3.借阅书籍\n");
printf("4.归还书籍\n");
printf("5.删除书籍\n");
printf("6.查找书籍\n");
printf("0.退出系统\n\n");
printf("请选择您想使用的功能0-8:");
}
//文件存操作
void saveInfoToFile(const char *fileName,struct Node* headNode) //将链表里的信息打印到文件里去
{
FILE *fp=fopen(fileName,"w"); //第一个指针使用写的方式打开文件
struct Node* pMove=headNode->next;
//当第二个节点不等于空时,就将链表里的信息打印到文件里去
while(pMove)
{
fprintf(fp,"%s\t%s\t%s\t%d\t%d\t%s\n",pMove->data.name,pMove->data.writter,pMove->data.publisher,pMove->data.price,pMove->data.num,pMove->data.introduction); //打印到指针文件指向的文件,打印的是pMove
pMove=pMove->next;
}
fclose(fp); //关闭文件
}
//文件读操作
void readInfoFromFile(const char* fileName,struct Node* headNode) //需要从文件里将内容读出
{
//把文件当作是一个输入键盘,定义临时变量tempData,把数据读到变量(如:%s)里
struct bookInfo tempData;
FILE* fp=fopen(fileName,"r"); //第一个指针使用读的方式打开文件
/*文件开始的时候是不存在的,不存在的时候,使用创建(w+)的读写方式打开文件*/
if(fp==NULL)
{
fp=fopen(fileName,"w+");
}
while(fscanf(fp,"%s\t%s\t%s\t%d\t%d\t%s\n",tempData.name,tempData.writter,tempData.publisher,tempData.price,tempData.num,tempData.introduction)!=EOF) //加上" != EOF"后该程序就不是死循环了
{
/*做链表的插入,读一个信息就插入到链表中*/
insertNodeByHead(list,tempData);
}
fclose(fp); //关闭文件
}
//按键处理,做交互
void keyDown()
{
int userKey=0;
//准备一个临时变量去存放书籍的临时信息
struct bookInfo tempBook;
//写个临时的指针用于查询
struct Node* result=NULL;
scanf("%d",&userKey);
switch (userKey)
{
case 0:
printf("\n-----退出-----\n");
printf("退出成功\n");
system("pause"); //防止闪屏
exit(0); //关闭掉整个程序
break;
case 1:
printf("\n-----登记-----\n");
printf("书名:");
scanf("%s",tempBook.name);
printf("作者:");
scanf("%s",tempBook.writter);
printf("出版社:");
scanf("%s",tempBook.publisher);
printf("价格/元:");
scanf("%d",&tempBook.price);
printf("数量:");
scanf("%d",&tempBook.num);
printf("简介(<100字):");
scanf("%s",tempBook.introduction);
//scanf("%s%f%d",tempBook.name,&tempBook.price,&tempBook.num); //tempBook.name不需要加&取地址,因为是字符串
insertNodeByHead(list, tempBook); // 插入链表
saveInfoToFile("bookinfo.txt",list); //当做了数据修改,就将信息保存到bookinfo.txt文件中,读到链表中
break;
case 2:
printf("\n-----浏览-----\n");
printList(list); //打印链表
break;
case 3:
printf("\n-----借阅-----\n"); //是把书的数量减少,书籍存在可以借,书的数量-1,不存在则提示借书失败
//通过书籍名去借阅
printf("请输入需要借阅的书名:");
scanf("%s",tempBook.name);
result=searchByName(list,tempBook.name);
if(result==NULL)
{
printf("没有此书籍,无法借阅!");
}
else
{
if(result->data.num>0) //大于0说明有书籍可借阅
{
result->data.num--; //书的数量-1
printf("借阅成功");
}
else
{
printf("当前书籍无库存,借阅失败");
}
}
break;
case 4:
printf("\n-----归还-----\n"); //是把当前书的数量增加+1
printf("请输入需要归还的书名:");
scanf("%s",tempBook.name);
result=searchByName(list,tempBook.name);
if(result==NULL)
{
printf("没有此书籍借阅记录,无法归还");
}
else
{
result->data.num++; //书的数量+1
printf("归还成功");
}
break;
case 5:
printf("\n-----删除-----\n");
printf("请输入要删除书籍的名字:");
scanf("%s",tempBook.name);
deleteNodeByName(list,tempBook.name);
saveInfoToFile("bookinfo.txt",list); //删除之后需要同步到文件,因为涉及到数据的修改
break;
case 6:
printf("\n-----查找-----\n");
printf("请输入要查询书籍的名字:");
scanf("%s",tempBook.name);
result=searchByName(list,tempBook.name);
//查找等效于链表的查找
//判断如果result等于NULL,就表示未找到
if(result==NULL)
{
printf("未找到该书籍");
}
else
{
printf("书名\t价格\t数量\n");
printf("%s\t%s\t%s\t%d\t%d\t%s\n",result->data.name,result->data.writter,result->data.publisher,result->data.price,result->data.num,result->data.introduction);
}
break;
default:
printf("\n-----错误-----\n"); //在switch语句中,如果表达式的值与每一中情况都不同,则执行default:后面的语句
break;
}
}
//调用测试实现
int main()
{
list=createHead();
readInfoFromFile("bookinfo.txt",list); //当系统运行的时候,就将文件的东西读到列表里
/*while(1)其中1代表一个常量表达式,
它永远不会等于0。循环会一直执行下去。
除非你设置break等类似的跳出循环语句循环才会中止。*/
while(1)
{
makeMenu();
keyDown();
system("pause");
system("cls"); //清屏代码:
}
system("pause"); //就是暂停程序的执行,等待任意健继续执行。
return 0;
}
在164行前面加上
struct Node* pMove= (struct Node*)malloc(sizeof(struct Node))