C语言|小型工资管理系统/学生管理系统
C语言结课作业:编写一个小型工资管理系统,用来管理职工的个人基本信息及工资薪酬等数据。在技术要求方面仅仅是要求了使用结构体数据类型,但基于我们的学习进度,考察的应该是用数组来储存。
作业要求
-
每个职工的数据应包括:工号、姓名、性别、部门、基本工资、绩效工资、奖金、应发工资(应发工资=基本工资+绩效工资+奖金)。职工数据要求采用结构体数据类型。
-
**菜单显示:**显示系统的一级功能菜单(数据输入、数据修改、数据处理、数据输出、退出)。
-
数据输入:至少输入10个职工的各项数据(应发工资由系统自动计算,不需要输入)。**录入的第一条记录用自已的真实姓名,部门为自己的真实班级。**并将有自己真实姓名、班级的输出结果截屏保存到大作业总结报告中。
-
数据修改:输入工号,修改指定工号的各项数据。输出修改完成后的全部职工数据。
-
数据处理:处理方式包括:排序、查询。可提供子菜单让用户选择。其中:
1)排序:按工号排序,显示所有职工的全部数据。
2)查询:按工号查询,显示指定工号的全部数据。
-
数据输出:输出全部职工的各项数据。
-
退出:退出整个工资管理系统。
整体框架
1.数据存储
我使用的是结构体数组来自建一个数据库,放在全局环境下,将数据库设为一个全局变量
//自建数据库
struct staff {
int id;
char name[20];
char sex[20];
char belong[20];
int basic_pay;
int add_pay;
int reward_pay;
int all_pay;
}data[999] = {
{44, "张三", "男", "技术部", 5000, 1000, 200, 6200 },
{14, "李四", "男", "运维部", 4000, 1000, 200, 5200 },
{56, "小红", "女", "网安部", 3000, 1000, 200, 4200 },
};
另外还需要定义一个全局的数据库长度
int lenth = 3;
之所以直接定义为一个常量,而不是用sizeof这种方式来计算数组长度,因为我发现结构体数组用这种方式来计算数组长度会是一个大坑!所以还不如手动添加常量
2.函数功能结构
(1)函数声明
//函数声明
int create(); //增
int del(); //删
int edit(); //改
int process(); //处理
int req(); //查
int line(); //排
int showall(); //查询全部
(2)函数定义
//菜单系统
int main(void){
int order;
while(1){
printf("****************************\n");
printf("主菜单:\n");
printf("1.输入员工信息\n");
printf("2.删除员工信息\n");
printf("3.修改员工信息\n");
printf("4.处理员工信息\n");
printf("5.显示所有员工信息\n");
printf("6.退出\n");
printf("****************************\n");
printf("---请输入菜单项:---\n");
scanf("%d",&order);
if(order==6){
system("cls");
return 0;
}
switch(order){
case 1: create();break;
case 2: del();break;
case 3: edit();break;
case 4: process();break;
case 5: showall(); break;
default: printf("\n404 NOT FOUND!"); getchar(); getchar(); system("cls");
}
}
}
//增添信息
int create() {}
//删除信息
int del(){}
//修改信息
int edit(){}
//处理信息
int process(){}
//员工信息排序输出
int line(){}
//查询员工信息
int req(){}
//查询全部数据
int showall(){}
函数结构如下
这些函数都会对全局中的数据库进行操作。
菜单系统
main函数这里,就构建整体的函数调用框架,即本管理系统的菜单系统
//菜单系统
int main(void){
int order;
while(1){
printf("****************************\n");
printf("主菜单:\n");
printf("1.输入员工信息\n");
printf("2.删除员工信息\n");
printf("3.修改员工信息\n");
printf("4.处理员工信息\n");
printf("5.显示所有员工信息\n");
printf("6.退出\n");
printf("****************************\n");
printf("---请输入菜单项:---\n");
scanf("%d",&order);
if(order==6){
system("cls");
return 0;
}
switch(order){
case 1: create();break;
case 2: del();break;
case 3: edit();break;
case 4: process();break;
case 5: showall(); break;
default: printf("\n404 NOT FOUND!"); getchar(); getchar(); system("cls");
}
}
}
菜单系统常用switch语句来调用各个函数,另外为了整个管理系统的输出整洁一点,我还用了
“stdlib.h”库中的清屏函数system(“cls”)
增添员工信息
增添员工信息的基本设计思路:将新的信息填入到下一个数组空的位置。
int create() {
int i;
i = lenth;
printf("\n");
printf("新增员工工号:");
scanf("%d", &data[i].id);
printf("\n");
printf("新增员工姓名:");
scanf("%s", &data[i].name);
printf("\n");
printf("新增员工性别:");
scanf("%s", &data[i].sex);
printf("\n");
printf("新增员工所属部门:");
scanf("%s", &data[i].belong);
printf("\n");
printf("新增员工基本工资:");
scanf("%d", &data[i].basic_pay);
printf("\n");
printf("新增员工绩效工资:");
scanf("%d", &data[i].add_pay);
printf("\n");
printf("新增员工奖金:");
scanf("%d", &data[i].reward_pay);
printf("\n");
data[i].all_pay = data[i].basic_pay + data[i].add_pay + data[i].reward_pay;
printf("创建成功!\n");
lenth++;
getchar();
getchar();
system("cls");
}
用在全局定义的lenth(突然发现自己记错了长度的英语单词2333)来确定新数据的填写地址,创建成功后要注意用lenth++
,拓展数据的长度。
删除员工信息
删除员工信息的基本设计思路是:通过遍历所有的数据查询到指定的员工工号,然后将后面的数据依次向上填补被“删除”的空白
//删除信息
int del(){
int i = lenth;
int num;
printf("\n请输入删除员工工号:");
scanf("%d", &num);
for(int j=0; j<=i+1; j++){
if(num==data[j].id){
for( ;j<=i-j; j++){
data[j] = data[j+1];
}
lenth--;
printf("\n删除成功!\n");
break;
}
if(j==i+1){
printf("\n查无此人!\n");
break;
}
}
getchar();
getchar();
system("cls");
}
在遍历中用到了两个if语句,if(num==data[j].id)来寻找符合条件的员工号,而if(j==i+1)
则是遍历所有的数据后未找到匹配条件。
说是删除实践上只是删除的数组元素的地位(下标)和财产(数据)被下一个数组元素给继承了,而下一个的数据元素的下标和数据又被下下个数据元素继承,以此类推。这里同样要注意用lenth--
缩短数据长度。
修改员工信息
修改员工信息的思路和删除员工信息的思路相差无几:都需要查询定位对应的员工信息,不同的是定位后,这个还要修改员工的信息
//修改信息
int edit(){
int i = lenth;
int num;
printf("\n请输入需要修改的员工工号:");
scanf("%d", &num);
printf("\n");
for(int j=0; j<=i+1; j++){
if(num==data[j].id){
printf("修改姓名:");
scanf("%s", &data[j].name);
printf("\n");
printf("修改性别:");
scanf("%s", &data[j].sex);
printf("\n");
printf("修改所属部门:");
scanf("%s", &data[j].belong);
printf("\n");
printf("修改基本工资:");
scanf("%d", &data[j].basic_pay);
printf("\n");
printf("修改绩效工资:");
scanf("%d", &data[j].add_pay);
printf("\n");
printf("修改奖金:");
scanf("%d", &data[j].reward_pay);
printf("\n");
data[j].all_pay = data[j].basic_pay + data[j].add_pay + data[j].reward_pay;
printf("修改成功!\n");
showall();
break;
}
if(j==i+1){
printf("\n查无此人!\n");
break;
}
}
getchar();
getchar();
system("cls");
}
所谓修改,无非是重新赋值罢了。
处理信息
处理信息分成了排序信息和查询信息两部分,所有采用菜单系统同样的方式来设计
//处理信息
int process(){
int order;
printf("---------------------------------------------\n");
printf("1.员工信息排序 2.员工信息查询\n");
printf("---------------------------------------------\n");
printf("请选择处理方式:");
scanf("%d",&order);
printf("\n");
switch(order){
case 1: line(); break;
case 2: req(); break;
}
}
1.信息排序
说实话这个函数的设计是该项目唯一卡到我的地方,因为我一直把它理解成了“排序处理”,但我了解实际上的需求应该是“排序输出”,那问题就简单很多了,我的设计思路如下
将每个数组元素的地址看作是它们的住址,而员工工号信息就是它们的门牌号,把它们的门牌号记录下来,然后排列大小顺序,按顺序去拜访它们,期间它们的住处时是没有发生改变的。
//员工信息排序输出
int line(){
int i;
int id_data[100];
i = lenth;
char* str[8] = {"工号", "姓名", "性别", "部门", "基本工资", "绩效工资", "奖金", "应发工资"};
for(int j = 0; j<i; j++){
id_data[j] = data[j].id;
}
qsort(id_data, i, sizeof(id_data[0]) , cmp);
printf("\n排序成功!\n\n") ;
//数据表的字段输出
for(int n=0; n<8; n++){
printf("%-12s" , str[n]);
}
printf("\n\n");
//数据排序输出
for(int n1 = 0; n1<i; n1++){
for(int n2 = 0; n2<i; n2++ ){
if(id_data[n1] == data[n2].id){
printf(" %-10d", data[n2].id);
printf(" %-10s", data[n2].name);
printf(" %-10s", data[n2].sex);
printf("%-10s", data[n2].belong);
printf(" %-10d", data[n2].basic_pay);
printf(" %-10d", data[n2].add_pay);
printf(" %-10d", data[n2].reward_pay);
printf(" %-10d", data[n2].all_pay );
printf("\n\n");
}
}
}
getchar();
getchar();
system("cls");
}
依据代码,我的思路对应关系如下
- 住址 ==> 数组元素下标,即
data
数组的下标 - 门牌号 ==>
data[j].id
- 记录门牌号 ==> 用了另一个数组:
id_data[j]
来储存 - 排列门牌号大小 ==> 将
id_data[j]
存储的门牌号排序,这里使用了C语言内置的排序函数:qsort() - 依次拜访 ==> 两层循环,外层循环:
for(int n1 = 0; n1<i; n1++)
循环名单上排列好的门牌号,内层循环:for(int n2 = 0; n2<i; n2++ )
,一个个去找符合名单上第一位的门牌号,然后输出。
另外注意,作为一个大学生的作业,使用内置的qsort()排序函数应该是非法的(狗头),应该使用冒泡排序或选择排序!
qsort()函数使用参考文章:点击查看
好,我摊牌啦,我就是想偷一下懒……
2.查询员工信息
查询的思路和删除信息,修改信息相似,没什么特别的地方
//查询员工信息
int req(){
int i = lenth;
int num;
char* str[8] = {"工号", "姓名", "性别", "部门", "基本工资", "绩效工资", "奖金", "应发工资"};
printf("\n请输入需要查询员工工号:");
scanf("%d", &num);
printf("\n\n");
for(int n=0; n<8; n++){
printf("%-12s" , str[n]);
}
printf("\n\n");
for(int j=0; j<=i+1; j++){
if(num==data[j].id){
printf(" %-10d", data[j].id);
printf(" %-10s", data[j].name);
printf(" %-10s", data[j].sex);
printf("%-10s", data[j].belong);
printf(" %-10d", data[j].basic_pay);
printf(" %-10d", data[j].add_pay);
printf(" %-10d", data[j].reward_pay);
printf(" %-10d", data[j].all_pay );
printf("\n\n");
printf("\n查询成功!\n\n");
break;
}
if(j==i+1){
printf(" 无\n");
printf("\n查无此人!\n");
break;
}
}
getchar();
getchar();
system("cls");
}
这里输出数据表的表头,用了数组来存储字符串,要注意加*号: char* str[8]
查询全部数据
无脑遍历输出即可,就是输出格式上要有调整,一般可以用printf("%-9d")
占9个空格,左对齐的方式来调整输出格式,输出那里我也是为了美观才一个个去加空格调整格式,就不要吐槽我代码格式不整洁了。
//查询全部数据
int showall(){
int i;
char* str[8] = {"工号", "姓名", "性别", "部门", "基本工资", "绩效工资", "奖金", "应发工资"};
i = lenth - 1;
data[i].all_pay = data[i].basic_pay + data[i].add_pay + data[i].reward_pay;
printf("\n");
printf("查询成功!\n\n");
for(int n=0; n<8; n++){
printf("%-12s" , str[n]);
}
printf("\n\n");
for(int j=0; j<=i; j++){
printf(" %-10d", data[j].id);
printf(" %-10s", data[j].name);
printf(" %-10s", data[j].sex);
printf("%-10s", data[j].belong);
printf(" %-10d", data[j].basic_pay);
printf(" %-10d", data[j].add_pay);
printf(" %-10d", data[j].reward_pay);
printf(" %-10d", data[j].all_pay );
printf("\n\n");
}
getchar();
getchar();
system("cls");
}
实现效果:
看这整整齐齐的亚子,爱了爱了!
总结
整体来说,这个结课作业并不难,只用用课堂上的知识完全是可以完成了(我一个下午不到就写好)。
但事实上无论用哪门语言,增删改查的实现永远是入门重要的第一课,本项目和之前的python学生管理系统,都仅仅是在内部实现的数据增删改查,一旦退出程序,缓存就被清除了,无法保留。所以我们还应考虑——如何把这样的操作在文本中实现,如何连接mysql数据库实现同样增删改查的操作。
所以呀,流水的编程语言,铁打的管理系统,入门从管理系统开始!