c语言实现银行账户管理(超详细)_用c语言设计一个模拟银行存取款的功能,最多可以对5000个银行账号的信息进行管理,-程序员宅基地

技术标签: c语言  数据结构  

目录

前言

一,需求定义

二,具体框架

1,Manager(管理员).h头文件和BankCard(银行卡).h头文件

(1)Manager.h

(2)BankCard.h

2,文件框架

三,函数封装

1,Manager.cpp

(1) 初始化数组

(2)登录管理员

(3)注册管理员

(4)销毁释放

2,BankCard.cpp

(1)初始化银行卡

(2)初始化该银行卡对应记录

(3)初始化卡内容

(4)登录

(5)记录流水记录

(6)注册

(7)存款

(8)取款

(9)转账

(10)查余额

(11)查开户日期

(12)查流水明细

(13)挂失卡

(14)注销卡

(15)结束时销毁数组

四,函数调用

1,前置工作

2,atm_op.cpp(引用头文件后的所有函数调用)

(1)从文件中读取管理员身份信息

(2)从文件中读取银行卡身份信息

(3)将Manager数据保存到文件中

(4)将BankCard数据保存到文件中

(5)判断是否是管理员

(6)判断是否是用户

(7)银行卡解锁

(8)管理员选项界面

(9)用户选项界面

(10)管理员功能选择实现

(11)用户功能选择实现

(12)打开atm机窗口

3,main主函数

五,心得总结


前言

熟练使用c语言所练习时写的,也是大学时c语言的课设题目,但是当时老师要求用链表写,那一版的要对数据结构里链表有足够的熟练才行。

我这版的是用数组结构体写的(数组的话对于代码本身实现很简单)。

一,需求定义

首先我们需要有一个管理员和用户的不同的操作模块,两者各自有各自的登录和实现功能。

                     

其次,我是用EasyX图形库调用窗口并贴以ATM机做背景图片以实现的,用鼠标点击来确认用户或管理员所需要实现的功能模块,图放这了。

二,具体框架

1,Manager(管理员).h头文件和BankCard(银行卡).h头文件

(1)Manager.h

宏定义INITSIZE和GROWSIZE分别是初始化管理员数组初始个数和扩容倍数。

#pragma once
#define INITSIZE 10
#define GROWSIZE 2
typedef struct Manager {
	int m_id;//管理员账户id
	int m_passwd;//账号密码
}Manager;
typedef struct Array_Manager {
	Manager* managers;//管理员
	int manager_length;//当前数组总容量
	int manager_size;//记录当前数组中有效数据个数
}Array_Manager;
//初始化数组
void InitManagers(Array_Manager* parr);
//登录管理员
bool LoginManager(Array_Manager* parr, int m_id, int m_passwd);
//注册管理员
bool RegisterManager(Array_Manager* parr, int m_id, int m_passwd);
//银行卡解锁
bool FindCard(Array_Manager* parr, int m_id, int m_passwd);
//销毁释放
void DestoryManagers(Array_Manager* parr);

(2)BankCard.h

宏定义RECORDSIZE:初始化流水记录个数

宏定义INITCARDSIZE:初始化银行卡个数

宏定义GROWCARDSIZE:银行卡扩容倍数

#pragma once
#define RECORDSIZE 100
#define INITCARDSIZE 10
#define GROWCARDSIZE 2
typedef struct User {
	char name[128];//姓名
	char idcard[128];//身份证号
	long long phone;//电话号码
}User;
typedef struct Record {
	const char* time;//日期
	const char* type;//业务
	double money;//金额
}Record;
typedef struct BankCard {
	int card_id;//卡号
	int card_passwd;//卡密
	double money;//余额
	int islocked;//是否被锁
	int islost;//是否丢失
	char create_card_date[20];//开户日期
	User user;//卡主
	Record* records;//流水记录
	int records_size;//记录当前数组中有效数据个数
	int records_length;//当前数组总容量
}BankCard;
typedef struct Array_BankCard {
	BankCard* cards;//银行卡
	int cards_length;//当前数组总容量
	int cards_size;//记录当前数组中有效数据个数
}Array_BankCard;
//初始化银行卡 
void InitCardArray(Array_BankCard* parr);
//初始化该银行卡对应记录
void InitRecords(BankCard* pcard);
//初始化卡内容
void InitCard(BankCard* pcard, int id, int passwd, double money, User* puser);
//登录
int LoginCard(Array_BankCard* parr, int id, int passwd);//返回银行卡存储下标
//注册
bool RegisterCard(Array_BankCard* parr, int id, int passwd, double money, User* puser);
//存款
bool SaveMoney(Array_BankCard* parr, int index, double money);
//取款
bool Withdraw(Array_BankCard* parr, int index, double money);
//转账
bool Transfer(Array_BankCard* parr, int index, int userid, double money);
//查余额
void GetMoney(Array_BankCard* parr, int index);
//查开户日期
char* GetCretCarDate(Array_BankCard* parr, int index);
//查流水明细
void GetWaterMessage(Array_BankCard* parr, int index);
//挂失卡
bool Lost(Array_BankCard* parr, int index);
//注销卡
bool DeleteCard(Array_BankCard* parr, const char* idcard);
//结束销毁数组
void Destroy(Array_BankCard* parr);

2,文件框架

三,函数封装

1,Manager.cpp

(1) 初始化数组

用指针传递管理员数组,malloc扩容。

assert是断言函数,防止未接收到参数。

//初始化数组
void InitManagers(Array_Manager* parr) {
	assert(parr != nullptr);
	parr->managers = (Manager*)malloc(INITSIZE * sizeof(Manager));
	assert(parr->managers != nullptr);
	//memset(parr->managers, 0, INITSIZE * sizeof(Manager));//初始化堆内存
	parr->manager_length = INITSIZE;//容量初始化
	parr->manager_size = 0;//有效个数0
}

(2)登录管理员

//登录管理员
bool LoginManager(Array_Manager* parr, int m_id, int m_passwd) {
	assert(parr != nullptr);
	bool flag = false;
	for (int i = 0; i < parr->manager_size; i++) {
		if (parr->managers[i].m_id == m_id && parr->managers[i].m_passwd == m_passwd) {
			flag = true;
			break;
		}
	}
	return flag;
}

(3)注册管理员

注册时需注意是否需要扩容。

//注册管理员
bool RegisterManager(Array_Manager* parr, int m_id, int m_passwd) {
	assert(parr != nullptr);
	if (parr->manager_size == parr->manager_length) {
		Manager* temp = (Manager*)realloc(parr->managers, GROWSIZE * parr->manager_length * sizeof(Manager));
		if (temp == nullptr) {
			return false;
		}
		parr->managers = temp;
		parr->manager_length *= GROWSIZE;
	}
	Manager manager = { m_id, m_passwd };
	parr->managers[parr->manager_size++] = manager;
	return true;
}

(4)销毁释放

//销毁释放
void DestoryManagers(Array_Manager* parr) {
	assert(parr != nullptr);
	free(parr->managers);
	parr->managers = nullptr;//防止野指针
}

2,BankCard.cpp

(1)初始化银行卡

//初始化银行卡 
void InitCardArray(Array_BankCard* parr) {
	assert(parr != nullptr);
	parr->cards = (BankCard*)malloc(INITCARDSIZE * sizeof(BankCard));
	assert(parr->cards != nullptr);
	parr->cards_length = INITCARDSIZE;//容量初始化
	parr->cards_size = 0;//有效个数0
}

(2)初始化该银行卡对应记录

//初始化该银行卡对应记录
void InitRecords(BankCard* pcard) {
	assert(pcard != nullptr);
	pcard->records = (Record*)malloc(RECORDSIZE * sizeof(Record));
	assert(pcard->records != nullptr);
	pcard->records_length = RECORDSIZE;//容量初始化
	pcard->records_size = 0;//有效个数0
}

(3)初始化卡内容

这里用到time有关函数,这个是用来直接获得当前系统所用时间的,因为要写开户日期是需要直接读取到本地系统时间的。

//初始化卡内容
void InitCard(BankCard* pcard, int id, int passwd, double money, User* puser) {
	*pcard = { id, passwd, money, false, false };
	char timestr[20] = { 0 };//2023-2-18 15:00
	struct tm* p;
	time_t t;
	time(&t);//获取当前时间
	p = localtime(&t);//时间数据换算为年月日格式
	sprintf(timestr, "%d-%d-%d%d:%d", p->tm_year, p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min);
	strcpy(pcard->create_card_date, timestr);
	pcard->user = *puser;
	InitRecords(pcard);//上述初始化卡
}

(4)登录

登录这里返回银行卡存储下标位置是方便下面进行其他功能时直接操作当前账号卡。

//登录
int LoginCard(Array_BankCard* parr, int id, int passwd) {//返回银行卡存储下标
	assert(parr != nullptr);
	int index = -1;
	for (int i = 0; i < parr->cards_size; i++) {
		if (parr->cards[i].card_id == id && parr->cards[i].card_passwd == passwd && parr->cards[i].islocked == false && parr->cards[i].islost == false) {
			index = i;
			break;
		}
	}
	return index;
}

(5)记录流水记录

//记录流水记录
void InsertRecords(BankCard* pcard, const char* type, double money) {
	char timestr[20] = { 0 };//2023-2-18 15:00
	struct tm* p;
	time_t t;
	time(&t);//获取当前时间
	p = localtime(&t);//时间数据换算为年月日格式
	sprintf(timestr, "%d-%d-%d %d:%d", p->tm_year, p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min);
	Record record = { timestr, type, money };
	pcard->records[pcard->records_size++] = record;
}

(6)注册

//注册
bool RegisterCard(Array_BankCard* parr, int id, int passwd, double money, User* puser) {
	assert(parr != nullptr);
	for (int i = 0; i < parr->cards_size; i++) {
		if (strcmp(parr->cards[i].user.idcard, puser->idcard) == 0) {
			return false;
		}
	}
	BankCard bankcard;
	InitCard(&bankcard, id, passwd, money, puser);
	if (parr->cards_size == parr->cards_length) {
		BankCard* temp = (BankCard*)realloc(parr->cards, GROWCARDSIZE * parr->cards_length * sizeof(BankCard));
		if (temp == nullptr) {
			return false;
		}
		parr->cards = temp;
		parr->cards_length *= GROWCARDSIZE;
	}
	InsertRecords(&bankcard, "开户", money);//记录流水记录
	parr->cards[parr->cards_size++] = bankcard;
	return true;
}

(7)存款

//存款
bool SaveMoney(Array_BankCard* parr, int index, double money) {
	assert(parr != nullptr);
	parr->cards[index].money += money;
	InsertRecords(&parr->cards[index], "存款", money);//记录流水记录
	return true;
}

(8)取款

//取款
bool Withdraw(Array_BankCard* parr, int index, double money) {
	assert(parr != nullptr);
	bool t = parr->cards[index].money < money ? false : true;
	if (t) {
		parr->cards[index].money -= money;
		InsertRecords(&parr->cards[index], "取款", money);//记录流水记录
	}
	return t;
}

(9)转账

找不到对方或者是余额不足是不能转出去的哦。

//转账
bool Transfer(Array_BankCard* parr, int index, int userid, double money) {
	assert(parr != nullptr);
	bool t = false;
	int i = 0;
	for (; i < parr->cards_size; i++) {
		if (parr->cards[i].card_id == userid) {
			break;
		}
	}
	if (i != parr->cards_size) {
		if (parr->cards[index].money > money) {
			parr->cards[i].money += money;
			parr->cards[index].money -= money;
			InsertRecords(&parr->cards[index], "转出", -1 * money);//记录流水记录
			InsertRecords(&parr->cards[i], "转入", money);//记录流水记录
			t = true;
		}
	}
	return t;
}

(10)查余额

void GetMoney(Array_BankCard* parr, int index) {
	assert(parr != nullptr);
	printf("%lf\n", parr->cards[index].money);
}

(11)查开户日期

//查开户日期
char* GetCretCarDate(Array_BankCard* parr, int index) {
	assert(parr != nullptr);
	return &parr->cards[index].create_card_date[0];
}

(12)查流水明细

//查流水明细
void GetWaterMessage(Array_BankCard* parr, int index) {
	assert(parr != nullptr);
	for (int i = 0; i < parr->cards[index].records_size; i++) {
		printf("%s %s, %lf\n", parr->cards[index].records->time, parr->cards[index].records->type, parr->cards[index].records->money);
	}
}

(13)挂失卡

//挂失卡
bool Lost(Array_BankCard* parr, int index) {
	assert(parr != nullptr);
	parr->cards[index].islocked = 1;
	parr->cards[index].islost = 1;
	return true;
}

(14)注销卡

这个注销卡我是只给了管理员这个权限的(前后文都有提及)。

//注销卡
bool DeleteCard(Array_BankCard* parr, const char* idcard) {
	assert(parr != nullptr);
	int i = 0;
	for (; i < parr->cards_size; i++) {
		if (strcmp(parr->cards[i].user.idcard, idcard) == 0) {
			break;
		}
	}
	free(parr->cards->records);
	for (; i < parr->cards_size; i++) {
		parr->cards[i] = parr->cards[i + 1];
	}
	parr->cards_size--;
	return true;
}

(15)结束时销毁数组

//结束销毁数组
void Destroy(Array_BankCard* parr) {
	assert(parr != nullptr);
	free(parr->cards->records);
	free(parr->cards);
	parr->cards->records = nullptr;//防止野指针
	parr->cards = nullptr;//防止野指针
}

四,函数调用

1,前置工作

这里为代码方便使用,我是直接将账号信息数据存贮到文件里去了。

所以在这里需要你们在自己桌面上建立两个文本文档,一个用来存贮管理员账号信息,一个用来存贮银行卡账号信息。

2,atm_op.cpp(引用头文件后的所有函数调用)

(1)从文件中读取管理员身份信息

这里直接把你们自己创建的文件的地址跟我的一替换就行。

//从文件中读取管理员身份信息
void ReadMangerFile(Array_Manager* parr) {
	assert(parr != nullptr);
	FILE* fp;  //用于文件操作
	int i = 0;//初始化,用于确定个数
	//从文件中取出账户信息并存入当前操作系统中
	/*a+:以附加方式打开可读/写的文件。若文件不存在,则会创建该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF符不保留)。*/
	if ((fp = fopen("C:/Users/123/Desktop/ATM机Manger账号信息存根.txt", "a+")) == NULL)
	{
		printf("数据文件无法打开!\n");//出错预处理
		exit(0);//exit函数直接结束当前进程
	}
	/*fscanf其功能为根据数据格式,从输入流(文件)中读入数据,存储到结构体数组中,遇到空格和换行时结束。*/
	while (fscanf(fp, "%d %d",  &(parr->managers + i)->m_id, &(parr->managers + i)->m_passwd) != EOF)
	{
		i++;
	}
	parr->manager_size = i; 
	fclose(fp);//关闭文件
	return;
}

(2)从文件中读取银行卡身份信息

//从文件中读取银行卡身份信息
void ReadBankCardFile(Array_BankCard* parr) {
	assert(parr != nullptr);
	FILE* fp;  //用于文件操作
	int i = 0;//初始化,用于确定个数
	//从文件中取出账户信息并存入当前操作系统中
	/*a+:以附加方式打开可读/写的文件。若文件不存在,则会创建该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF符不保留)。*/
	if ((fp = fopen("C:/Users/123/Desktop/ATM机BankCard账号存根.txt", "a+")) == NULL)
	{
		printf("数据文件无法打开!\n");//出错预处理
		exit(0);//exit函数直接结束当前进程
	}
	/*fscanf其功能为根据数据格式,从输入流(文件)中读入数据,存储到结构体数组中,遇到空格和换行时结束。*/
	while (fscanf(fp, "%d %d %s %d %d %lf %d %d %s %s %lld", &(parr->cards + i)->card_id, &(parr->cards + i)->card_passwd, &(parr->cards + i)->create_card_date, 
		&(parr->cards + i)->islocked, &(parr->cards + i)->islost, &(parr->cards + i)->money, &(parr->cards + i)->records_length, 
		&(parr->cards + i)->records_size, &(parr->cards + i)->user.idcard, &(parr->cards + i)->user.name, &(parr->cards + i)->user.phone) != EOF)
	{
		InitRecords(parr->cards + i);
		i++;
	}
	parr->cards_size = i;
	fclose(fp);//关闭文件
	return;
}

(3)将Manager数据保存到文件中

void WriteMangerFile(Array_Manager* parr) {
	assert(parr != nullptr);
	FILE* fp;  //用于文件操作
	int i = 0;
	//保存余额
	/*w:打开只写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。*/
	if ((fp = fopen("C:/Users/123/Desktop/ATM机Manger账号信息存根.txt", "w")) == NULL)
	{
		printf("数据文件无法打开!\n");
		exit(0);//exit函数直接结束当前进程
	}
	for (i = 0; i < parr->manager_size; i++) {
		fprintf(fp, "%d", (parr->managers + i)->m_id);
		fprintf(fp, " %d\n", (parr->managers+ i)->m_passwd);
	}
	fclose(fp);//关闭文件
}

(4)将BankCard数据保存到文件中

void WriteBankCardFile(Array_BankCard* parr) {
	assert(parr != nullptr);
	FILE* fp;  //用于文件操作
	int i = 0;
	//保存余额
	/*w:打开只写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。*/
	if ((fp = fopen("C:/Users/123/Desktop/ATM机BankCard账号存根.txt", "w")) == NULL)
	{
		printf("数据文件无法打开!\n");
		exit(0);//exit函数直接结束当前进程
	}
	for (i = 0; i < parr->cards_size; i++) {
		fprintf(fp, "%d", (parr->cards + i)->card_id);
		fprintf(fp, " %d", (parr->cards + i)->card_passwd);
		fprintf(fp, " %s", (parr->cards + i)->create_card_date);
		fprintf(fp, " %d", (parr->cards + i)->islocked);
		fprintf(fp, " %d", (parr->cards + i)->islost);
		fprintf(fp, " %lf", (parr->cards + i)->money);
		fprintf(fp, " %d", (parr->cards + i)->records_length);
		fprintf(fp, " %d", (parr->cards + i)->records_size);
		fprintf(fp, " %s", (parr->cards + i)->user.idcard);
		fprintf(fp, " %s", (parr->cards + i)->user.name);
		fprintf(fp, " %lld\n", (parr->cards + i)->user.phone);
	}
	fclose(fp);//关闭文件
}

(5)判断是否是管理员

EasyX图形库里面,InputBox这个函数就是弹出一个输入框让你输入,这个是自动识别为字符数组类型的,我这里是用atoi函数强转成的int整型。

MessageBoxA函数就是给一个选项框,MB_OK就是只有一个“确定”选项。

//判断是否是管理员
void IsManger(Array_Manager* parr) {
	assert(parr != nullptr);
	//弹出输入账号窗口界面
	while (true) {
		char buff[128] = { 0 };
		InputBox(buff, 128, "账号");
		int value1 = atoi(buff);
		char buff2[128] = { 0 };
		InputBox(buff2, 128, "密码");
		int value2 = atoi(buff2);
		bool a = LoginManager(parr, value1, value2);
		if (a)
			break;
		else {
			MessageBoxA(NULL, "账号密码有误!是否重输?", "账号密码不正确", MB_OK);//a作为返回值进行确定下一界面的调用
		}
	}
}

(6)判断是否是用户

//判断是否是用户
int IsUser(Array_BankCard* parr) {
	assert(parr != nullptr);
	//弹出输入账号窗口界面
	while (true) {
		char buff[128] = { 0 };
		InputBox(buff, 128, "账号");
		int value1 = atoi(buff);
		char buff2[128] = { 0 };
		InputBox(buff2, 128, "密码");
		int value2 = atoi(buff2);
		int a = LoginCard(parr, value1, value2);
		if (a != -1) {
			return a;
		}
		else {
			MessageBoxA(NULL, "账号密码有误或此卡已丢失上锁!是否重输?", "请确认是否是您的卡", MB_OK);//a作为返回值进行确定下一界面的调用
		}
	}
}

(7)银行卡解锁

//银行卡解锁
bool FindCard(Array_BankCard* parr, int m_id, int m_passwd) {
	assert(parr != nullptr);
	int index = -1;
	for (int i = 0; i < parr->cards_size; i++) {
		if (parr->cards[i].card_id == m_id && parr->cards[i].card_passwd == m_passwd) {
			index = i;
			break;
		}
	}
	if (index != -1) {
		parr->cards[index].islocked = 0;
		parr->cards[index].islocked = 0;
		return true;
	}
	else 
		return false;
}

(8)管理员选项界面

(我觉得我在这里写的注释应该可以看懂吧?)

//管理员选项界面
int Mangeroption() {
	//矩形(多用于按钮) rectangle(起点x, 起点y, 终点x, 终点y) 自带边框填充fillrectangle(同上)
	RECT rect1 = { 110, 210, 165, 230 };
	fillrectangle(110, 210, 165, 230);
	RECT rect2 = { 435, 210, 490, 230 };
	fillrectangle(435, 210, 490, 230);
	RECT rect3 = { 110, 250, 165, 270 };
	fillrectangle(110, 250, 165, 270);
	RECT rect4 = { 435, 250, 490, 270 };
	fillrectangle(435, 250, 490, 270);
	RECT rect5 = { 110, 290, 165, 310 };
	fillrectangle(110, 290, 165, 310);
	settextcolor(BLACK);//设置文字颜色
	settextstyle(15, 5, "楷体");//设置文字大小
	setbkmode(TRANSPARENT);//设置文字透明背景
	//文字存放在矩形中间
	drawtext("注册银行卡", &rect1, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	drawtext("注销银行卡", &rect2, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	drawtext("注册管理员", &rect3, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	drawtext("银行卡解锁", &rect4, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	drawtext("退出登录", &rect5, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	//鼠标事件监听 左键 右键
	while (true) {
		MOUSEMSG msg = GetMouseMsg();
		//鼠标操作
		if (msg.x >= 110 && msg.x <= 165 && msg.y >= 210 && msg.y <= 230 && msg.uMsg == WM_LBUTTONDOWN) {
			return 1;
		}
		if (msg.x >= 435 && msg.x <= 490 && msg.y >= 210 && msg.y <= 230 && msg.uMsg == WM_LBUTTONDOWN) {
			return 2;
		}
		if (msg.x >= 110 && msg.x <= 165 && msg.y >= 250 && msg.y <= 270 && msg.uMsg == WM_LBUTTONDOWN) {
			return 3;
		}
		if (msg.x >= 435 && msg.x <= 490 && msg.y >= 250 && msg.y <= 270 && msg.uMsg == WM_LBUTTONDOWN) {
			return 4;
		}
		if (msg.x >= 110 && msg.x <= 165 && msg.y >= 290 && msg.y <= 310 && msg.uMsg == WM_LBUTTONDOWN) {
			return 0;
		}
	}
}

(9)用户选项界面

//用户选项界面
int BankCardoption() {
	RECT rect1 = { 110, 210, 165, 230 };
	fillrectangle(110, 210, 165, 230);
	RECT rect2 = { 435, 210, 490, 230 };
	fillrectangle(435, 210, 490, 230);
	RECT rect3 = { 110, 250, 165, 270 };
	fillrectangle(110, 250, 165, 270);
	RECT rect4 = { 435, 250, 490, 270 };
	fillrectangle(435, 250, 490, 270);
	RECT rect5 = { 110, 290, 165, 310 };
	fillrectangle(110, 290, 165, 310);
	RECT rect6 = { 435, 290, 490, 310 };
	fillrectangle(435, 290, 490, 310);
	RECT rect7 = { 110, 330, 165, 350 };
	fillrectangle(110, 330, 165, 350);
	RECT rect8 = { 435, 330, 490, 350 };
	fillrectangle(435, 330, 490, 350);
	settextcolor(BLACK);//设置文字颜色
	settextstyle(15, 5, "楷体");//设置文字大小
	setbkmode(TRANSPARENT);//设置文字透明背景
	//文字存放在矩形中间
	drawtext("存款", &rect1, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	drawtext("取款", &rect2, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	drawtext("转账", &rect3, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	drawtext("查余额", &rect4, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	drawtext("查开户日期", &rect5, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	drawtext("查流水明细", &rect6, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	drawtext("挂失", &rect7, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	drawtext("退出", &rect8, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	while (true) {
		MOUSEMSG msg = GetMouseMsg();
		//鼠标操作
		if (msg.x >= 110 && msg.x <= 165 && msg.y >= 210 && msg.y <= 230 && msg.uMsg == WM_LBUTTONDOWN) {
			return 1;
		}
		if (msg.x >= 435 && msg.x <= 490 && msg.y >= 210 && msg.y <= 230 && msg.uMsg == WM_LBUTTONDOWN) {
			return 2;
		}
		if (msg.x >= 110 && msg.x <= 165 && msg.y >= 250 && msg.y <= 270 && msg.uMsg == WM_LBUTTONDOWN) {
			return 3;
		}
		if (msg.x >= 435 && msg.x <= 490 && msg.y >= 250 && msg.y <= 270 && msg.uMsg == WM_LBUTTONDOWN) {
			return 4;
		}
		if (msg.x >= 110 && msg.x <= 165 && msg.y >= 290 && msg.y <= 310 && msg.uMsg == WM_LBUTTONDOWN) {
			return 5;
		}
		if (msg.x >= 435 && msg.x <= 490 && msg.y >= 290 && msg.y <= 310 && msg.uMsg == WM_LBUTTONDOWN) {
			return 6;
		}
		if (msg.x >= 110 && msg.x <= 165 && msg.y >= 330 && msg.y <= 350 && msg.uMsg == WM_LBUTTONDOWN) {
			return 7;
		}
		if (msg.x >= 435 && msg.x <= 490 && msg.y >= 330 && msg.y <= 350 && msg.uMsg == WM_LBUTTONDOWN) {
			return 0;
		}
	}
}

(10)管理员功能选择实现

管理员是最高级权限,可以对银行卡进行销户和注册以及挂失的操作。

再提一嘴,MessageBoxA函数这里MB_YESNO就是有一个“是”选项和一个“否”选项。这里要注意是的话会返回6,否的话会返回7。注意是7不是0!!!

//管理员功能选择实现
void ManagerWork(Array_Manager* parr, Array_BankCard* parr2) {
	assert(parr != nullptr && parr2 != nullptr);
	IsManger(parr);
	while (true) {
		int t = Mangeroption();
		switch (t) {
		case 0:
			break;
		case 1:
		{
			char buff[128] = { 0 };
			InputBox(buff, 128, "账号");
			int id = atoi(buff);
			char buff2[128] = { 0 };
			InputBox(buff2, 128, "密码");
			int passwd = atoi(buff2);
			char buff3[128] = { 0 };
			InputBox(buff3, 128, "金额");
			double money = atof(buff3);
			User user = { NULL, NULL, 0 };
			InputBox(user.name, 128, "姓名");
			InputBox(user.idcard, 128, "身份证号");
			char buff6[128] = { 0 };
			InputBox(buff6, 128, "电话号码");
			user.phone = atoll(buff6);
			bool test = RegisterCard(parr2, id, passwd, money, &user);
			if (test) {
				MessageBoxA(NULL, "恭喜你,注册成功!", "注册成功", MB_OK);
			}
			else {
				MessageBoxA(NULL, "很抱歉,您的身份证已存在一张卡!", "注册失败", MB_OK);
			}
			break;
		}
		case 2:
		{
			char buff4[128] = { 0 };
			InputBox(buff4, 128, "请输入您要注销的账号");
			bool deletecard = DeleteCard(parr2, buff4);
			if (deletecard) {
				MessageBoxA(NULL, "恭喜你,注销成功!", "注销成功", MB_OK);
			}
			else {
				MessageBoxA(NULL, "很抱歉,注销失败,请确认您的卡号是否正确", "注销失败", MB_OK);
			}
			break;
		}
		case 3:
		{
			char buff7[128] = { 0 };
			InputBox(buff7, 128, "账号");
			int id2 = atoi(buff7);
			char buff8[128] = { 0 };
			InputBox(buff8, 128, "密码");
			int passwd2 = atoi(buff8);
			RegisterManager(parr, id2, passwd2);
			MessageBoxA(NULL, "恭喜你,注册成功!", "注册成功", MB_OK);
			break;
		}
		case 4:
		{
			char buff9[128] = { 0 };
			InputBox(buff9, 128, "账号");
			int id3 = atoi(buff9);
			char buff10[128] = { 0 };
			InputBox(buff10, 128, "密码");
			int passwd3 = atoi(buff10);
			bool find = FindCard(parr2, id3, passwd3);
			if (find) {
				MessageBoxA(NULL, "恭喜你,解锁成功!", "解锁成功", MB_OK);
			}
			else {
				MessageBoxA(NULL, "请确认您所输入的账号和密码是否正确!", "解锁失败", MB_OK);
			}
			break;
		}
		}
		if (t != 0) {
			int n = MessageBoxA(NULL, "还有别的服务需求吗?", "ATM机管理员询问", MB_YESNO);//a作为返回值进行确定下一界面的调用
			if (n == 6) {
				continue;
			}
			else break;
		}
		else break;
	}
}

(11)用户功能选择实现

//用户功能选择实现
void BankCardWord(Array_BankCard* parr) {
	assert(parr != nullptr);
	int index = IsUser(parr);
	while (true) {
		int t = BankCardoption();
		switch (t) {
		case 0:
			break;
		case 1:
		{
			char buff[128] = { 0 };
			InputBox(buff, 128, "金额");
			double money = atof(buff);
			bool save = SaveMoney(parr, index, money);
			if (save) {
				MessageBoxA(NULL, "恭喜你,存款成功!", "存款", MB_OK);
			}
			else {
				MessageBoxA(NULL, "很抱歉,存款失败!", "存款失败", MB_OK);
			}
			break;
		}
		case 2:
		{
			char buff2[128] = { 0 };
			InputBox(buff2, 128, "金额");
			double money2 = atof(buff2);
			bool get = Withdraw(parr, index, money2);
			if (get) {
				MessageBoxA(NULL, "恭喜你,取款成功!", "取款", MB_OK);
			}
			else {
				MessageBoxA(NULL, "您的余额不足,取款失败!", "取款失败", MB_OK);
			}
			break;
		}
		case 3:
		{
			char buff3[128] = { 0 };
			InputBox(buff3, 128, "请输入你要转向的账户");
			int userid = atoi(buff3);
			char buff4[128] = { 0 };
			InputBox(buff4, 128, "金额");
			double money3 = atof(buff4);
			bool tranfer = Transfer(parr, index, userid, money3);
			if (tranfer) {
				MessageBoxA(NULL, "恭喜你,转账成功!", "转账", MB_OK);
			}
			else {
				MessageBoxA(NULL, "您的余额不足或不存在该账户,转账失败!", "转账失败", MB_OK);
			}
			break;
		}
		case 4:
			GetMoney(parr, index);
			break;
		case 5:
		{
			char* create = GetCretCarDate(parr, index);
			int i = 0;
			for (; i < 8; i++) {
				printf("%c", *(create + i));
			}
			printf(" ");
			for (; i < strlen(parr->cards[index].create_card_date); i++) {
				printf("%c", *(create + i));
			}
			printf("\n");
			break;
		}
		case 6:
			GetWaterMessage(parr, index);
			break;
		case 7:
		{
			bool lost = Lost(parr, index);
			if (lost) {
				MessageBoxA(NULL, "不用担心,已将您挂失的卡成功上锁!", "挂失", MB_OK);
			}
			else {
				MessageBoxA(NULL, "很抱歉,挂失失败!", "挂失失败", MB_OK);
			}
			break;
		}
		}
		if (t != 0) {
			int n = MessageBoxA(NULL, "还有别的服务需求吗?", "ATM机管理员询问", MB_YESNO);//a作为返回值进行确定下一界面的调用
			if (n == 6) {
				continue;
			}
			else break;
		}
		else break;
	}
}

(12)打开atm机窗口

initgraph函数就是建立一个图形化窗口。

SHOWCONSOLE就是把控制台也展示出来。

image在这里简单理解就是贴了个图,图片就是前面放的那张ATM机的图片,直接下载就好,如果你要用自己的图那么我只能祝你好运了(因为我前面有关选项按钮的设置是根据我自己的图打的点,打点本身其实不难,数学功底够硬就好,主要还是一个点一个点打的太麻烦,懒人直接下载我的图就好)。

//打开atm机窗口
void Put_initgraph(Array_Manager* parr, Array_BankCard* parr2) {
	assert(parr != nullptr && parr2 != nullptr);
	//BGM();
	initgraph(600, 700, SHOWCONSOLE);
	IMAGE image;
	loadimage(&image, "F:/微信图片_20230218171935.jpg", 600, 700, true);
	putimage(0, 0, &image);
	while (true) {
		int m = MessageBoxA(NULL, "你是管理员吗?", "ATM识别身份", MB_YESNO);//a作为返回值进行确定下一界面的调用
		if (m == 6) {
			ManagerWork(parr, parr2);
			int next = MessageBoxA(NULL, "是否需要更换身份?选否则运行结束", "登录", MB_YESNO);
			cleardevice();//清屏
			loadimage(&image, "F:/微信图片_20230218171935.jpg", 600, 700, true);
			putimage(0, 0, &image);
			if (next == 6)
				continue;
			else
				break;
		}
		else {
			int u = MessageBoxA(NULL, "你是用户吗?", "ATM识别身份", MB_YESNO);//a作为返回值进行确定下一界面的调用
			if (u == 6) {
				BankCardWord(parr2);
				int next2 = MessageBoxA(NULL, "是否需要更换身份?选否则运行结束", "登录", MB_YESNO);
				cleardevice();//清屏
				loadimage(&image, "F:/微信图片_20230218171935.jpg", 600, 700, true);
				putimage(0, 0, &image);
				if (next2 == 6)
					continue;
				else
					break;
			}
			else {
				MessageBoxA(NULL, "你绝对是选错了,没有第三个身份。", "逗我玩呢?", MB_OK);
			}
		}
	}
	system("pause");//按任意键继续
	closegraph();//关闭窗口
}

3,main主函数

int main() {
	Array_Manager managers;
	InitManagers(&managers);
	ReadMangerFile(&managers);
	Array_BankCard bankcards;
	InitCardArray(&bankcards);
	ReadBankCardFile(&bankcards);
	Put_initgraph(&managers, &bankcards);
	WriteMangerFile(&managers);
	DestoryManagers(&managers);
	WriteBankCardFile(&bankcards);
	Destroy(&bankcards);
	return 0;
}

五,心得总结

作为c语言的练习代码,其中封装函数和调用函数是必须要掌握的基础知识,相较于我大学时写的简易版的银行账户管理系统,我现在这个版本也只是加上了EasyX库的图形调用。

整体上来说功能模块完成的还算完整,缺点是没有对卡的流水记录进行文件存贮,会导致重新运行时数据的丢失(不过问题不大,只要你学会了文件读写操作,就是多谢一部分代码的事)。

整体代码量比较长,但是为了功能完整性,还是只能写这么多

 最后:

(本文仅供学习时参考,如有错误,纯属作者技术不到位,不足之处请多指教,勿喷,谢谢)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_70339595/article/details/129760433

智能推荐

攻防世界_难度8_happy_puzzle_攻防世界困难模式攻略图文-程序员宅基地

文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文

达梦数据库的导出(备份)、导入_达梦数据库导入导出-程序员宅基地

文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作  导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释:   cwy_init/init_123..._达梦数据库导入导出

js引入kindeditor富文本编辑器的使用_kindeditor.js-程序员宅基地

文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js

STM32学习过程记录11——基于STM32G431CBU6硬件SPI+DMA的高效WS2812B控制方法-程序员宅基地

文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6

计算机网络-数据链路层_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输

软件测试工程师移民加拿大_无证移民,未受过软件工程师的教育(第1部分)-程序员宅基地

文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...

随便推点

Thinkpad X250 secure boot failed 启动失败问题解决_安装完系统提示secureboot failure-程序员宅基地

文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure

C++如何做字符串分割(5种方法)_c++ 字符串分割-程序员宅基地

文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割

2013第四届蓝桥杯 C/C++本科A组 真题答案解析_2013年第四届c a组蓝桥杯省赛真题解答-程序员宅基地

文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答

基于供需算法优化的核极限学习机(KELM)分类算法-程序员宅基地

文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。

metasploitable2渗透测试_metasploitable2怎么进入-程序员宅基地

文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入

Python学习之路:从入门到精通的指南_python人工智能开发从入门到精通pdf-程序员宅基地

文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf

推荐文章

热门文章

相关标签