C语言调试时出现”Unhandled exception 0xC0000005;Access Violation“,是怎么回事呢?
语法没错,调试时输入后,跳出一个提示“Unhandled exception 0xC0000005;Access Violation”直接运行时,输入后,自动停止程序。
这是怎么回事呢?求大家指导指导!!!先谢了!!!
代码如下:
#include "windows.h"
#include "stdio.h"
#include "stdlib.h"
#include "conio.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -1
#define MAX_NAME_LEN 20 // 姓名最大长度
#define MAX_BKNAME_LEN 30 // 书名最大长度
#define MAX_BOOKS 100 // 书库中一个著者最多著作数
#define KEEP_DAYS 90 // 图书出借的期限
#define logfile "LibraryLogs.log" // 系统日志文件
typedef int Status;
char *books[MAX_BOOKS]; // 某位著者著作名指针数组
char author[MAX_NAME_LEN]; // 著者姓名数组
int books_counter; // 著者著作计数
typedef struct ReaderNode // 借阅者
{
int cardnum; // 借阅证号
char Readername[MAX_NAME_LEN]; // 借阅者姓名
union{
struct{
struct ReaderNode *nextr; // 下一个借阅者指针
};
struct{
struct ReaderNode *nextb; // 下一个预约者指针
};
};
}ReaderNode,*ReaderType; // 读者类型
typedef struct BookNode // 图书结构体
{
int booknum; // 书号
char bookname[MAX_BKNAME_LEN]; // 书名
char writer[MAX_NAME_LEN]; // 著者名
int current, total; // 现存量和总库存
int publishyear; // 出版年份
float price; // 定价
ReaderType reader; // 读者链表指针
ReaderType appointmenter; // 预约者链表指针
} BookNode,*BookType; // 图书类型
#define m 3 // 定义3叉B树
typedef BookNode Record; // 记录指针为图书结点类型
typedef int KeyType;
typedef struct BTNode // B树结点
{
int keynum; // 每个结点关键字个数
struct BTNode *parent; // 父亲指针
KeyType key[m+1]; // 关键字数组,0号单元未用
struct BTNode *ptr[m+1]; // 子数指针
Record *rec[m+1]; // 记录指针,0号单元未用
}BTNode,*BTree; // B树节点类型和B树类型
typedef BTree Library;
typedef struct
{
BTNode *pt; // 指向找到的结点或应该插入的结点
int i; // 关键字序号
int tag; // 1表示查找成功,0表示查找失败
}Result; // B树查找结果类型
void NewRoot(BTree T, BTree p, KeyType k, BTree ap,Record *rec)
// 当插入B树时T为空或根结点分裂为q和ap两个节点,需建立一个根节点空间
// 本函数为T申请一块空间,插入p,k,ap和记录rec
{
T = (BTree)malloc(sizeof(BTNode));
T->keynum = 1;
T->ptr[0] = p; // 插入
T->ptr[1] = ap;
T->key[1] = k;
T->rec[1] = rec;
if (p) p->parent= T; // 刷新T的子树ap的父亲指针
if (ap) ap->parent = T;
T->parent = NULL; // 根节点双亲为NULL
}
void Insert(BTree q, int i, KeyType k, BTree ap, Record *rec)
// 将k和ap分别插入到q->key[i+1]和q->ptr[i+1],并插入关键字为k的记录rec
{
int j;
for(j = q->keynum;j > i; j--) // 记录、关键字、子树指针后移
{
q->key[j+1] = q->key[j];
q->ptr[j+1] = q->ptr[j];
q->rec[j+1] = q->rec[j];
}
q->key[i+1] = k; // 插入
q->ptr[i+1] = ap;
q->rec[i+1] = rec;
q->keynum ++; // 关键字个数增1
if(ap) ap->parent = q; // 刷新q的子树ap的父亲指针
}
void Split(BTree q, int n, BTree ap)
// 以n为分界将结点q分裂为q和ap2个结点
{
int i;
ap = (BTree)malloc(sizeof(BTNode)); // 新申请ap空间
ap->ptr[0] = q->ptr[n];
for(i = n+1;i <= m; i++) // q上n后的关键字、子树指针、记录转移到ap
{
ap->key[i-n] = q->key[i];
ap->ptr[i-n] = q->ptr[i];
ap->rec[i-n] = q->rec[i];
}
ap->keynum = q->keynum - n; // 计算ap的关键字个数
q->keynum = n-1; // q的关键字个数减少
ap->parent = q->parent;
for (i=0; i<=m-n; i++)
if(ap->ptr[i]) ap->ptr[i]->parent = ap; // 刷新ap的子树的父亲指针
}
int Search(BTree p, KeyType k)
// 在B树p结点中查找关键字k的位置i,使key[i]<=k<key[i+1])
{
int i;
for(i=0; i < p->keynum && (p->key[i+1] < k||p->key[i+1] == k); i++);
return i;
}
Status InsertBTree(BTree T, KeyType k, BTree q, int i,Record *rec)
// 在m阶B树T上结点*q的key[i]与key[i+1]之间插入关键字K和记录rec。
// 若引起结点过大,则沿双亲链进行必要的结点分裂调整,使T仍是m阶B树。
{
BTree ap = NULL;
int finished = FALSE;
if (!q) NewRoot(T, NULL, k, NULL,rec); // T是空树,生成仅含关键字K的根结点*T
else{
while (!finished)
{
Insert(q, i, k, ap,rec); // 将k和ap分别插入到q->key[i+1]和q->ptr[i+1]
if (q->keynum < m) finished = TRUE; // 插入完成
else{
Split(q, (m+1)/2, ap); // 分裂结点Q 调用前面的
k = q->key[(m+1)/2];
rec = q->rec[(m+1)/2];
if (q->parent)
{ // 在双亲结点*q中查找k的插入位置
q = q->parent;
i = Search(q, k);
}
else finished = OVERFLOW; // 根节点已分裂为*q和*ap两个结点
}
}
if (finished == OVERFLOW) // 根结点已分裂为结点*q和*ap
NewRoot(T, q, k, ap,rec); // 需生成新根结点*T,q和ap为子树指针
}
return OK;
} // InsertBTree
Result SearchBTree(BTree T, KeyType k)
// 在m阶B树上查找关键字k,返回结果(pt,i,tag)。若查找成功,则特征值tag=1,指针pt所指结点中第i个关键字等于k;
// 否则返回特征值tag=0,等于k的关键字应插入在pt所指结点中第i和第i+1个关键字之间。
{
int i = 1;
BTree p = T, q = NULL; // 初始化,p指向待查结点,q指向p的双亲
int found = FALSE;
while(p && !found)
{
i = Search(p, k); // 查找k的位置使p->key[i]<=k<p->key[i+1]
if(i> 0 && k == p->key[i]) found = TRUE;
else{ // 未找到,则查找下一层
q = p;
p = p->ptr[i];
}
}
if(found) {Result r = {p, i, 1}; return r;} // 查找成功
else {Result r = {q, i, 0}; return r;} // 查找不成功,返回k的插入位置信息
}
void TakePlace(BTree q, int i)
// *q结点的第i个关键字为k,用q的后继关键字替代q,且令q指向后继所在结点,
{
BTree p = q;
q = q->ptr[i];
while(q->ptr[0]) q = q->ptr[0]; // 搜索p的后继
p->key[i] = q->key[1]; // 关键字代替
p->rec[i] = q->rec[1]; // 记录代替
i = 1; // 代替后应该删除q所指结点的第1个关键字
}
void Del(BTree q, int i)
// 删除q所指结点第i个关键字及其记录
{
for(;i < q->keynum ;i++) // 关键字和记录指针前移
{
q->key[i] = q->key[i+1];
q->rec[i] = q->rec[i+1];
}
q->keynum --; // 关键字数目减1
}
Status Borrow(BTree q)
// 若q的兄弟结点关键字大于(m-1)/2,则从兄弟结点上移最小(或最大)的关键字到双亲结点,
// 而将双亲结点中小于(或大于)且紧靠该关键字的关键字下移至q中,并返回OK,否则返回EREOR。
{
int i;
BTree p = q->parent, b; // p指向q的双亲结点
for(i = 0 ; p->ptr[i] != q;i++) ; // 查找q在双亲p的子树位置
if(i >= 0 && i+1 <= p->keynum && p->ptr[i+1]->keynum > (m-1)/2)
{ // 若q的右兄弟关键字个数大于(m-1)/2
b = p->ptr[i+1]; // b指向右兄弟结点
q->ptr[1] = b->ptr[0]; // 子树指针也要同步移动
q->key[1] = p->key[i+1]; // 从父节点借第i+1个关键字
q->rec[1] = p->rec[i+1];
p->key[i+1] = b->key[1]; // b第一个关键字上移到父节点
p->rec[i+1] = b->rec[1];
for(i =1 ;i <= b->keynum;i++) // b第一个关键字上移,需把剩余记录前移一位
{
b->key[i] = b->key[i+1];
b->rec[i] = b->rec[i+1];
b->ptr[i-1] = b->ptr[i];
}
}
else if(i > 0 && p->ptr[i-1]->keynum > (m-1)/2)
{ // 若q的左兄弟关键字个数大约(m-1)/2
b = p->ptr[i-1]; // b指向左兄弟结点
q->ptr[1] = q->ptr[0];
q->ptr[0] = b->ptr[b->keynum];
q->key[1] = p->key[i]; // 从父节点借第i个关键字
q->rec[1] = p->rec[i];
p->key[i] = b->key[b->keynum]; // 将b最后一个关键字上移到父节点
p->rec[i] = b->rec[b->keynum];
}
else return ERROR; // 无关键字大于(m-1)/2的兄弟
q->keynum ++;
b->keynum --;
for(i = 0 ;i <=q->keynum; i++)
if(q->ptr[i]) q->ptr[i]->parent = q; // 刷新q的子结点的双亲指针
return OK;
}
void Combine(BTree q)
// 将q剩余部分和q的父结点的相关关键字合并到q兄弟中,然后释放q,令q指向修改的兄弟
{
int i, j ;
BTree p = q->parent, b; // p指向q的父亲
for(i = 0; p->ptr[i] != q; i++) ; // 插好q在父亲p中的子树位置
if(i == 0) // 如为0,则需合并为兄弟的第一个关键字
{
b = p->ptr[i+1];
for(j = b->keynum ; j >= 0 ;j--) // 将b的关键字和记录后移一位
{
b->key[j+1] = b->key[j];
b->rec[j+1] = b->rec[j];
b->ptr[j+1] = b->ptr[j];
}
b->ptr[0] = q->ptr[0]; // 合并
b->key[1] = p->key[1];
b->rec[1] = p->rec[1];
}
else if(i > 0) // 若q在父亲的子树位置大约0
{ // 需合并为兄弟b的最后一个关键字
b = p->ptr[i-1];
b->key[b->keynum+1] = p->key[i]; // 合并
b->rec[b->keynum+1] = p->rec[i];
b->ptr[b->keynum+1] = q->ptr[0];
}
if(i == 0 || i == 1) // 若i为0或1,需将父节点p关键字前移一位
for( ; i < p->keynum; i++)
{
p->key[i] = p->key[i+1];
p->ptr[i] = p->ptr[i+1];
p->rec[i] = p->rec[i+1];
}
p->keynum --;
b->keynum ++;
free(q);
q = b; // q指向修改的兄弟结点
for(i = 0;i <= b->keynum; i++)
if(b->ptr[i]) b->ptr[i]->parent = b; // 刷新b的子结点的双亲指针
}
Status DeleteBTree(BTree T,KeyType k)
// 在m阶B树T上删除关键字k及其对应记录,并返回OK。如T上不存在关键字k,则返回ERROR。
{
KeyType x=k;
BTree q,b = NULL;
int finished = FALSE,i = 1;
Result res = SearchBTree(T,k); // 在T中查找关键字k
if(res.tag == 0 ) return ERROR; // 未搜索到
else
{
q = res.pt; // q指向待删结点
i = res.i;
if(q->ptr[0]) TakePlace(q, i); // 若q的子树不空,(非底层结点)
// 则以其后继代之,且令q指向后继所在结点
Del(q,i); // 删除q所指向结点中第i个关键字及记录
if(q->keynum>=(m-1)/2||!q->parent) // 若删除后关键字个数不小于(m-1)/2或q是根节点
{
finished = TRUE; // 删除完成
if(q->keynum == 0 ) T = NULL; // 若q的关键字个数为0 ,则为空树
}
while(!finished)
{
if(Borrow(q)) finished = TRUE; // 若q的相邻兄弟结点关键字大于(m-1)/2,则从该
// 兄弟结点上移一个最大(或最小)关键字到
// 父节点,从父节点借一关键字到q
else{ // 若q相邻兄弟关键字个数均等于┌m /2┑-1
Combine(q); // 将q中的剩余部分和双亲中的相关关键字合并至q的一个兄弟中
q = q->parent; // 检查双亲
if(q == T && T->keynum ==0 ) // 若被删结点的父节点是根T且T的关键字个数为0
{
T = T->ptr[0]; // 新根
T->parent = NULL;
free(q); // 删除原双亲结点
finished = TRUE;
}
else if(q->keynum >= m/2) finished = TRUE;
} // 合并后双亲关键字个数不少于(m-1)/2,完成
}
}
return OK ;
}
void ShowBTree(BTree T,short x)
// 递归以凹入表形式显示B树T,每层的缩进量为x,初始缩进量为8
{
int i;
x = x+7;
if(!T) return ;
printf("\n");
for(i = 0;i<=x;i++)
putchar(' '); // 缩进x
for(i = 1 ;i <= T->keynum;i++)
printf("%d,",T->key[i]);
for(i = 0 ;i <= T->keynum;i++) // 递归显示子树结点关键字
ShowBTree(T->ptr[i],x);
}
void InitLibrary(Library L)
// 初始化书库L为空书库。
{
L = NULL;
}
void InsertBook(Library L ,BookType B , Result res)
// 书库L已存在,res包含B书在书库L中的位置或应该插入的位置
// 如果书库中已存在B书,则只将B书的库存量增加,否则插入B书到书库L中。
{
if(res.tag == 0)
InsertBTree(L, B->booknum, res.pt, res.i, B); // 如果书库中不存在该书,则插入
else{ // 如果已存在
BookType b = res.pt->rec[res.i];
b->current = b->current + B->total; // 现存量和总库存增加
b->total = b->total + B->total;
}
}
Status DeleteBook(Library L ,BookType B)
// 如果书库中存在B书,则从书库中删除B书的信息,并返回OK,否则返回ERROR
{
if(DeleteBTree(L,B->booknum)) return OK; // 如果删除成功,返回OK
else return ERROR; // 否则(删除不成功)返回ERROR
}
int BorrowBook(Library L ,BookType B ,ReaderType R)
// 书库L存在,B书是书库中的书并且可被读者R借阅
// 借出一本B书,登记借阅者R的信息,改变现存量,
{
if(B->current > 0) // 若现存量大于0
{
B->reader = R;
B->current--; // 现存量减1
}
return TRUE;
}
int ReturnBook(Library L ,int b ,int r,BookType B ,ReaderType R)
// B为还书书号,R为还书者借阅证号, 若书库中不存在书号为B的书,则返回-1
// 若有R借阅B书的记录,则注销该记录,并用B和R返回图书信息和借阅者信息并返回1,
// 若没有r借阅b书的记录,则用B返回图书信息,并返回0
{
ReaderType pre,p;
Result res = SearchBTree(L, b); // 搜索
if(!res.tag)
return -1; // 未搜索到,返回-1
B = res.pt->rec[res.i]; // 用B记录图书信息
p=res.pt->rec[res.i]->reader;
for( ; p ;pre = p,p = p->nextr) // 搜索借书者链表
if(p->cardnum == r) // 找到则用R返回借阅者信息
{
R = p;
pre ->nextr = p->nextr;
B->current++; // 现存量增1
return 1;
}
return 0; // 无该读者借阅该书信息则返回0
}
void Menu()
// 显示图书管理系统菜单
{
system("cls");
printf("\n");
printf(" ╔══════════════╗\n");
printf(" ║ 欢迎使用图书管理系统 ║\n");
printf(" ╚══════════════╝\n");
printf(" \n\n\n");
printf("\t 友情提示:本系统可进行的操作如下(1-9):\n");
printf("\t *****************************\n");
printf("\t * * \n");
printf("\t * 1 新书入库 * \n");
printf("\t * * \n");
printf("\t * 2 清除库存 * \n");
printf("\t * * \n");
printf("\t * 3 图书出借 * \n");
printf("\t * * \n");
printf("\t * 4 图书归还 * \n");
printf("\t * * \n");
printf("\t * 5 退出系统 * \n");
printf("\t * * \n");
printf("\t ***************************** \n");
}
void PrintH()
// 打印图书表格表头
{
printf("\n");
printf(" ╭═══════════════╮ \n");
printf("╭═════════║ 【 图书信息 】 ║════════════╮");
printf("║───┬─────╰═══════════════╯───┬────┬───║");
printf("║书号 │ 书名 │ 著者 │现存│总库存│出版年份│定价 ║");
}
void PrintT()
// 打印图书表格表尾
{
printf("║───┼───────────┼──────┼──┼───┼────┼───║");
printf("╰══════════════════════════════════════╯\n");
}
void PrintD(BookType B )
// 显示B书的基本信息。
{
printf("║───┼───────────┼──────┼──┼───┼────┼───║");
printf("║ %-4d │《%s》",B->booknum, B->bookname);
//gotoxy(32,wherey());
printf("│ %-11s│%-4d│ %-4d │%-6d │%-6.1f║",B->writer,
B->current,B->total,B->publishyear,B->price);
}
void PrintBook(BookType B)
// 以表格形式显示一本书的基本信息(书号,书名,著者,现存量,总库存量,出版年份,价格)
{
PrintH(); // 表头
PrintD(B); // 数据
PrintT(); // 表尾
printf("\n");
}
int main()
{
Library L;
int booknum,cardnum;
char in;
BookType B;
Result res;
ReaderType R;
short x=8; //初始缩进量为8
int k;
InitLibrary(L); // 初始化书库L
while(1)
{
Menu(); // 显示菜单
in = getch();
system("cls");
switch(in-'0') // 判断用户选择
{
case 1: // 图书入库
while(in != 'M' && in != 'm')
{
B = (BookType)malloc(sizeof(BookNode));
B->reader = NULL; // 下一个借阅者指针置空
printf("\n\n\t请输入要入库的书号:");
scanf("%d",&B->booknum);
res = SearchBTree(L, B->booknum); // 查找入库书号
if(res.tag) // 书库中已存在该书号的书
{
PrintBook(res.pt->rec[res.i]); // 显示这本书
printf("\n\n\t该书已存在如上,请输入新增入库册数: ");
fflush(stdin);
scanf("%d",&B->total);
InsertBook(L, B, res); // 该图书入库,数量增加
free(B);
}
else{ // 书库中不存在该书号,则插入到书库L中
fflush(stdin);
printf("\n\t请输入该书 书名: ");
gets(B->bookname);
printf("\n\t请输入该书著者: ");
fflush(stdin);
gets(B->writer);
printf("\n\t请输入该书册数: ");
fflush(stdin);
scanf("%d",&B->current);
B->total = B->current;
printf("\n\t插入后B树如下:\n\n");
ShowBTree(L,x); // 显示插入后B树状态
}
printf("\n\n\t图书入库完成,按M键返回主菜单,按其他任意键继续图书入库....");
in = getch();
}
break;
case 2: // 清除库存
while(in != 'M' && in != 'm')
{
printf("\n\n\t请输入要清除库存图书书号: ");
scanf("%d",&booknum);
res = SearchBTree(L, booknum); // 查找用户输入的书号
if(res.tag) // 如果查找到
{
B = res.pt->rec[res.i];
PrintBook(B); // 显示找到的书
printf("\t确认删除上面的图书<Y/N>?"); // 提示是否确认删除
in = getch();
if(in == 'Y' || in == 'y') // 如果确认删除
{
DeleteBook(L, B); // 删除图书
printf("\n\n\t图书%d从书库中清除完毕!\n\n\t删除后B树如下",booknum);
ShowBTree(L,x); // 显示删除后B树状态
}
}
else printf("\n\n\t书库中不存在书号为%d的书!",booknum);
printf("\n\n\t按'M'返回主菜单,按其他任意键继续清除库存...");
in = getch();
}
break;
case 3: // 图书出借
while(in != 'M' && in != 'm')
{
system("cls");
printf("\n\n\t请输入要借阅的图书书号: ");
scanf("%d",&booknum);
res = SearchBTree(L, booknum); // 在书库中搜索图书booknum
if(res.tag) // 如果找到
{
R = (ReaderType)malloc(sizeof(ReaderNode)); // 新申请一个读者空间
R->nextr = NULL; // 下一个借阅者指针置空
B = res.pt->rec[res.i];
printf("\n\n\t您查找的图书如下:");
PrintBook(B); // 显示找到的图书
printf("\n\n\t请输入您的借书证号:"); // 读入借阅者信息
scanf("%d",&R->cardnum);
printf("\n\n\t请输入您的姓名: ");
gets(R->Readername);
if(BorrowBook(L, B, R)) // 如果该借阅者可以借阅该书
{
printf("\n\n\t借书成功!");
}
else{
printf("\n\n\t对不起,您不能借阅该书!该书现存量少于0或已被他人预约。");
free(R); // 释放该读者空间
}
}
else printf("\n\n\t书库中不存在图书%d!",booknum);
printf("\n\n\t按'M'返回主菜单,按其他任意键继续借阅图书...");
in = getch();
}
break;
case 4: // 图书归还
while(in != 'M' && in != 'm')
{
system("cls");
printf("\n\n\t请输入你要归还的图书号: ");
scanf("%d",&booknum);
printf("\n\n\t请输入你的借书证号: ");
scanf("%d",&cardnum);
k = ReturnBook(L, booknum, cardnum, B, R);// 为读者cardnum还书
if(k == 1) // 如果还书成功
{
printf("\n\n\t还书成功!");
free(R); // 释放该读者借书记录
}
else if(k == 0) // 如果没有该读者借阅该书的记录
{
R = (ReaderType)malloc(sizeof(ReaderNode));
R->cardnum = cardnum;
strcpy(R->Readername,"###");
printf("\n\n\t没有您借图书%d的记录!",booknum);
free(R);
}
else printf("\n\n\t书库中不存在图书%d!",booknum);
printf("\n\n\t按'M'返回主菜单,按其它任意键继续还书...");
in = getch();
}
break;
case 9:
system("cls");
printf("\n\n\n\n\n\t退出系统,确认<Y/N>?..."); // 提示是否确认退出系统
in = getch();
if(in == 'y' ||in == 'Y')
{
// RecordLogs(8); // 记录日志-退出系统
exit(0); // 退出
}
break;
default: break;
}
}
return 0;
}