【C语言】网吧管理系统-链表项目设计(三)

简介: C语言 & 网吧管理系统

3. 计费管理

接下来是比较复杂的两个环节,计费管理以及查询统计~


计费管理操作上机下机,下机的时候计费~


需求:

记录上机记录

记录下机消费记录

这些记录就是为了查询统计而生的~

引入新的源文件:ChargeManage.c


源码在这:网吧管理系统/ChargeManage.c · 游离态



3.1 两个记录性结构体~

3.1.1 Consume 消费记录(basis.h)

typedef struct Consume {
  int id;//卡号
  long long  timeNode;//时间节点
  double money;//下机则为实际收费,合理是相同的
  struct Consume* next;//后继
}Consume;

卡号

对应交易时间时间戳

交易金额(按比例/按单位)

后继节点~

3.1.2 Puncher 上机记录(basis.h)

//上机记录
typedef struct Puncher {
  int id;//卡号
  long long  timeNode;//时间节点
  struct Puncher* next;//后继
}Puncher;


卡号

对应上机时间时间戳

后继节点~

3.2 主体函数设计~

chargeManage(ChargeManage.c)


引入新的全局变量


orderNumber消费单数~

workNumber上机记录数据数~

导入卡号,消费记录,上机记录~


根据菜单进行选择~


上机

下机

退出则更新释放三条链表~

void chargeManage(Manager* pm) {
  int input = 0;
  Card* pc = cardCarry();
  Consume* psu = consumeCarry();
  Puncher* ppu = puncherCarry();
  do {
  menu3_1();
  scanf("%d", &input);
  switch (input) {
  case 1 :
    onComputer(pc, ppu);
    break;
  case 2 :
    offComputer(pc, psu);
    break;
  default:
    printf("输入错误\n");
    break;
  case 0:
    exitOutCard(pc);
    exitOutConsume(psu);
    exitOutPuncher(ppu);
    printf("退出成功\n");
    break;
  }
  } while (input);
}



3.2.1 读取文件操作

3.2.1.1 consumeCarry(Carry.c)

读取data文件中的consume.txt二进制文件

Consume* consumeCarry() {
  FILE* pf = fopen("data\\consume.txt", "rb");
  Consume* psu = (Consume*)malloc(sizeof(Consume));//堆区空间,不会被收回
  if (pf == NULL) {
  pf = fopen("data\\consume.txt", "wb");
  fwrite(&orderNumber, sizeof(int), 1, pf);
  fclose(pf);
  }
  else {
  fread(&orderNumber, sizeof(int), 1, pf);
  fread(psu, sizeof(Consume), 1, pf);
  Consume* cur = psu;
  for (int i = 1; i < orderNumber; i++) {
    Consume* newOne = (Consume*)malloc(sizeof(Consume));
    fread(newOne, sizeof(Consume), 1, pf);
    newOne->next = NULL;
    cur->next = newOne;
    cur = cur->next;
  }
  }
  return psu;
}


文件指针为NULL代表打不开文件,说明文件不存在

则我只需要建立一个,并且将整数0导入文件中~

我的一个习惯:我会讲元素个数放在文件的首位

文件打开了,进行标准的读取操作

首先拿到消费单数orderNumber~

然后获得第一单~

后续以尾插的形式进行插入~

后续应该判断第一个节点是否是有效数据~

重点:要将尾节点的后驱置为NULL,否则会导致野指针异常/死循环~

3.2.1.2 puncherCarry(Carry.c)

读取data文件中的puncher.txt二进制文件

Puncher* puncherCarry() {
  FILE* pf = fopen("data\\puncher.txt", "rb");
  Puncher* ppu = (Puncher*)malloc(sizeof(Puncher));//堆区空间,不会被收回
  if (pf == NULL) {
  pf = fopen("data\\puncher.txt", "wb");
  fwrite(&workNumber, sizeof(int), 1, pf);
  fclose(pf);
  }
  else {
  fread(&workNumber, sizeof(int), 1, pf);
  fread(ppu, sizeof(Puncher), 1, pf);
  Puncher* cur = ppu;
  for (int i = 1; i < workNumber; i++) {
    Puncher* newOne = (Puncher*)malloc(sizeof(Puncher));
    fread(newOne, sizeof(Puncher), 1, pf);
    newOne->next = NULL;
    cur->next = newOne;
    cur = cur->next;
  }
  }
  return ppu;
}


与刚才是一样的~

文件指针为NULL代表打不开文件,说明文件不存在

则我只需要建立一个,并且将整数0导入文件中~

我的一个习惯:我会讲元素个数放在文件的首位

文件打开了,进行标准的读取操作

首先拿到上机记录数据数workNumber~

然后获得第一份数据~

后续以尾插的形式进行插入~

后续应该判断第一个节点是否是有效数据~

重点:要将尾节点的后驱置为NULL,否则会导致野指针异常/死循环~

3.3 菜单

上下机操作菜单


提醒顾客过早下机会导致浪费~

void menu3_1() {
  printf("****************************************\n");
  printf("※ 请提示顾客:时间较长的卡请勿过早下机\n"
  "电脑时刻开着呢,不需要再次上机\n");
  printf("0. 退出\n");
  printf("1. 上机\n");
  printf("2. 下机\n");
  printf("****************************************\n");
}


3.4 上机

conComputer(ChargeMange.c)


输入卡号密码确认是否符合身份

上机中/已注销无法再上机

将上机记录信息记录在ppu中~

上机成功后用时间函数报当时时间信息

找不到即报无~

void onComputer(Card* pc, Puncher* ppu) {
  printf("请输入卡号与密码:>");
  Card* cur = pc;
  int id = 0;
  char password[7];
  scanf("%d%s", &id, password);
  while (cardNumber != 0 && cur != NULL) {
  if (cur->id == id && strcmp(password, cur->password) == 0) {
    if (cur->effect != 1) {
    printf("此卡已被注销,无法上机\n");
    return;
    }
    else {
    if (cur->state == 1) {
      printf("已经是上机状态,本次操作失效\n");
    }
    cur->state = 1;
    time_t t = time(NULL);
    cur->upTime = t;
    //上机记录~~
    clockIn(cur, ppu);
    //上机记录~~
    printf("--------------------------\n");
    printf("上机成功\n");
    printf("时间:%s", ctime(&t));
    printf("--------------------------\n");
    }
    return;
  }
  cur = cur->next;
  }
  printf("上机失败,“可能”原因是此卡暂为开通\n");
}



3.4.1 上机记录函数clockIn(ChargeManage.c)

void clockIn(Card* cur, Puncher* ppu) {
  Puncher* newOne = (Puncher*)malloc(sizeof(Puncher));
  newOne->id = cur->id;
  newOne->next = NULL;
  newOne->timeNode = cur->upTime;
  if (workNumber == 0) {
  memcpy(ppu, newOne, sizeof(Puncher));
  }
  else {
  Puncher* current = ppu;
  while (current->next != NULL) {
    current = current->next;
  }
  current->next = newOne;
  }
  workNumber++;
}


重点要处理workNumber为0时的那个假数据!

用memcpy(库里内存函数,string.h)

新节点newOne记录信息后,尾插到链表尾

workNumber上机记录数据数 + 1

3.5 下机

offnComputer(ChargeMange.c)


输入卡号密码确认是否符合身份

未上机/卡的计费标准不存在的情况下,无法下机

将消费记录信息记录在psu中~

对于余额此刻小于0后,强制注销~

下机成功后用时间函数报当时时间信息

找不到即报无~

void offComputer(Card* pc, Consume* psu) {
  printf("请输入卡号与密码:>");
  Card* cur = pc;
  int id = 0;
  char password[7];
  scanf("%d%s", &id, password);
  Standard* pcs = standardCarry();
  while (cardNumber != 0 && cur != NULL) {
  if (cur->id == id && strcmp(password, cur->password) == 0) {
    if (cur->state == 0) {
    printf("已经是下机状态,本次操作失效\n");
    return;
    }
    if (pcs[cur->cardType - 1].state == 1) {//未上机
    time_t t = time(NULL);
    long long longTime = t - cur->upTime;
    double gap = 1.0 * longTime / transfer(cur->cardType);
    if (gap != (int)gap) {
      gap = (int)gap + 1;
    }
    //记录收益记录~~
    settlement(cur, psu, t, gap, pcs);
    //记录收益记录~~
    cur->state = 0;
    if (cur->balance < 0) {
      printf("此卡已欠费,账号已被注销,请充值缴费\n");
      cur->effect = 0;
    }
    printf("--------------------------\n");
    printf("下机成功, 账户已更新\n");//下机则结算
    printf("时间:%s", ctime(&t));
    printf("--------------------------\n");
    }
    else {
    printf("下机失败~请补充计费标准~\n");
    }
    exitOutStandard(pcs);//释放空间
    return;
  }
  cur = cur->next;
  }
  printf("下机失败,“可能”原因是此卡暂为开通\n");
}



3.5.1 transfer(计费标准转化秒数函数 ChargeManage.c)

//3600------1h
//86400-----1day
//2592000---1month//默认30天
//31536000--1year//默认365天
long long transfer(int i) {
  switch (i) {
  case 1 :  
    return 31536000;
  case 2 :  
    return 2592000;
  case 3 :  
    return 86400;
  case 4 :  
    return 3600;
  }
}



3.5.2 局部变量:gap

例如年卡,一年以内gap为1,一年到两年之间gap为2,恰好为两年gap为2~

表示几个单价~

time_t t = time(NULL);
  long long longTime = t - cur->upTime;
  double gap = 1.0 * longTime / transfer(cur->cardType);
  if (gap != (int)gap) {
    gap = (int)gap + 1;//不足进1~
  }


3.5.3 下机消费记录函数settlement(ChargeManage.c)

void settlement(Card* cur, Consume* psu, time_t t, double gap, Standard* pcs) {
    double benifit = gap * pcs[cur->cardType - 1].price;
  cur->balance -= benifit;
  Consume* newOne = (Consume*)malloc(sizeof(Consume));
  newOne->id = cur->id;
  newOne->money = benifit;
  newOne->next = NULL;
  newOne->timeNode = t;
  if (orderNumber == 0) {
  memcpy(psu, newOne, sizeof(Consume));
  }
  else {
  Consume* current = psu;
  while (current->next != NULL) {
    current = current->next;
  }
  current->next = newOne;
  }
  orderNumber++;
}

根据gap和计费标准pcs确定交易金~

制作newOne新节点

将新节点插入到psu的链表尾

处理orderNumber为0时头结点为假数据的情况~

orderNumber 消费单数 + 1

3.6 退出操作

对三链表进行释放更新处理~

卡链表的退出已在上文书写

3.6.1 消费单表的更新

exitOutConsume(Exit.c)


数据导入data(必须自己建立)文件的consume.txt二进制文件中


如果卡数为0,那么只讲0导入文件即可,因为这个节点不为NULL但是不是有效数据!


不为0遍历链表导入数据,并且用相同的方法释放空间~


void exitOutConsume(Consume* psu) {
  FILE* pf = fopen("data\\consume.txt", "wb");
  fwrite(&orderNumber, sizeof(int), 1, pf);
  if (orderNumber == 0) {
  return;
  }
  while (psu != NULL) {
  fwrite(psu, sizeof(Consume), 1, pf);
  Consume* tmp = psu;
  psu = psu->next;
  free(tmp);
  }
  fclose(pf);
}

3.6.2 上机记录数据表

exitOutPunche(Exit.c)


数据导入data(必须自己建立)文件的puncher.txt二进制文件中


如果卡数为0,那么只讲0导入文件即可,因为这个节点不为NULL但是不是有效数据!


不为0遍历链表导入数据,并且用相同的方法释放空间~

void exitOutPuncher(Puncher* ppu) {
  FILE* pf = fopen("data\\puncher.txt", "wb");
  fwrite(&workNumber, sizeof(int), 1, pf);
  if (workNumber == 0) {
  return;
  }
  while (ppu != NULL) {
  fwrite(ppu, sizeof(Puncher), 1, pf);
  Puncher* tmp = ppu;
  ppu = ppu->next;
  free(tmp);
  }
  fclose(pf);
}


3.7 补充:注销导致的强制下机计费

前面下机导致的注销,信息已记录

但是注销导致的下机,尚未解决(下面操作出现在卡管理的注销操作中~)


导入消费单表


记录强制下机消费记录


释放更新消费单consume.txt~


3.7.1 强制下机消费记录函数 off (CardManage.c)

void off(Card* pc, Consume* psu) {
  printf("由于此次下机非主动下机,所以此次消费以按比例计算\n");
  Standard* pcs = standardCarry();
  time_t t = time(NULL);
  long long longTime = t - pc->upTime;
  double gap = 1.0 * longTime / transfer(pc->cardType);
  settlement(pc, psu, t, gap, pcs);
  pc->state = 0;
}


为什么gap的类型我要定位double,原因就是我还要再次使用settlement函数

这里我规则是:注销导致强制下机,交易金是以时间占比计算的~

以此计算gap~

调用settlement下机消费记录函数~

并将卡的上机状态改为0 =>下机~

3.8 测试

卡的情况


测试结果正常~

至于消费记录以及上机记录,再查询统计的时候一起测试~


二进制文件显示正常~


5. 查询统计

来到最后一个环节啦啦啦~ ==> 查询统计

查询单一卡一段时间的消费

查询全部卡一段时间额总消费

查询近一年来每个月的营销额以及上机次数~

引出一个新的源文件:SearchStatistics.c

源码在这:网吧管理系统/SearchStatistics.c · 游离态


5.1 主体函数设计

searchStatistics(SearchStatistics.c)


导入卡链表

根据菜单选择对应操作~

void searchStatistics(Manager* pm) {
  int input = 0;
  Card* pc = cardCarry();
  void (*func[4])(Card * pc) = 
  { exitOutStatistics, searchConsume, statisticsTime, statisticsMonths };
  do {
  menu5_1();
  scanf("%d", &input);
  if (input <= 3 && input >= 0) {
    func[input](pc);
  }
  else {
    printf("输入失败\n");
  }
  } while (input);
}


5.1.1 函数指针数组

这里由于选项较多,我选择用函数指针数组,用法与刚才一样,要注意下标与菜单与对应选项要相符合哦~

5.2 菜单

统计菜单

void menu5_1() {

printf("******************\n");

printf("0. 退出\n");

printf("1. 查询消费记录\n");

printf("2. 统计总营业额\n");

printf("3. 统计月营业额\n");

printf("******************\n");

}


选择时间段菜单

void menu5_2() {

printf("******************\n");

printf("1. 最近一年\n");

printf("2. 最近一月\n");

printf("3. 最近一天\n");

printf("******************\n");

}


5.3 查询单一卡一段时间的消费~

searchConsume(SearchStatistics.c)

输入卡号,探路指针找到对应卡~


找到后通过菜单选择一个时间段

调用consumeCarry去导入消费记录链表

通过消费记录链表节点的时间属性去判断是否属于该时间段

若属于且是对应卡,则以表格打印出来~

该行用箭头指向右侧的结算时间~

打印思路:(打印的同时用中间寄存器释放空间)

如何对齐请看上文~


找不到报无~


void searchConsume(Card* pc) {
    //三个选择,最近一天,最近一月,最近一年
  printf("请输入一张卡的卡号:>");
  int id = 0;
  scanf("%d", &id);
  Card* cur1 = pc;
  while (cardNumber != 0 && cur1 != NULL) {
  if (id == cur1->id) {
    menu5_2();
    int number = 0;
    printf("请选择一个时间段(距现在):>");
    scanf("%d", &number);
    long long gapTime = transfer(number);
    Consume* psu = consumeCarry();
    Consume* cur2 = psu;
    printf("+------+-------------+\n");
    printf("|%-6s|%13s|\n", "卡号", "消费/元");
    printf("+------+-------------+\n");
    while (cur2 != NULL) {
    Consume* tmp = cur2;
    if (cur2->id == id && time(NULL) - cur2->timeNode <= gapTime) {
      printf("|%-6d|%13.2lf| 结算时间===>%s", cur2->id, cur2->money, ctime(&cur2->timeNode));
      printf("+------+-------------+\n");
    }
    cur2 = cur2->next;
    free(tmp);
    }
    return;
  }
  cur1 = cur1->next;
  }
  printf("查询失败,暂无此卡\n");
}


5.3.1 细讲判断时间段方法~


调用transfer,将选择的数字转化为对应时间戳(一个单位时间)

如果满足要求且是对应的那张卡,打印下来~

5.4 查询所有卡一段时间的总消费

statisticsTime(SearchStatistics.c)


通过菜单选择一个时间段

调用consumeCarry去导入消费记录链表

引入新的局部变量sum,计算总营业额~

通过消费记录链表节点的时间属性去判断是否属于该时间段

若属于,则以表格打印出来~

该行用箭头指向右侧的结算时间~

打印思路:(打印的同时用中间寄存器释放空间)

如何对齐请看上文~



并在最后,打印该时间段的总营业额~

void statisticsTime(Card* pc) {
  menu5_2();
  int number = 0;
  printf("请选择一个时间段统计总营业额(距现在):>");
  scanf("%d", &number);
  Consume* psu = consumeCarry();
  Consume* cur = psu;
  printf("+------+-------------+\n");
  printf("|%-6s|%13s|\n", "卡号", "消费/元");
  printf("+------+-------------+\n");
  double sum = 0;
  long long gapTime = transfer(number);
  while (cur != NULL) {
  Consume* tmp = cur;
  if (time(NULL) - cur->timeNode <= gapTime) {
    printf("|%-6d|%13.2lf| 结算时间===>%s", cur->id, cur->money, ctime(&cur->timeNode));
    printf("+------+-------------+\n");
    sum += cur->money;
  }
  cur = cur->next;
  free(tmp);
  }
  printf("|%-6s|%13.2lf|\n", "共计", sum);
  printf("+------+-------------+\n");
}



5.5 查询统计近一年来每个月的营销额以及上机次数~

statisticsMonths(SearchStatistics.c)


别看代码多,其实模块化很明确~

后面细腻分析~

大致思路是

通过计算获取现在是第几年第几个月

然后推导出近12个月

通过十二个月每一个特定的时间戳范围查询统计划分~

导入消费记录和上机数据记录~

遍历多次出打印结果~

void statisticsMonths(Card* pc) {
  Date date = judgeMonth(time(NULL));
  int year = date.year;
  int month = date.month;
  Date dates[12];
  dates[11] = date;
  for (int i = 10; i >= 0; i--) {
  month--;
  if (month == 0) {
    year--;
    month = 12;
  }
  dates[i].timestamp = dates[i + 1].timestamp - months[judgeLeapYear(year)][month] * 86400;
  dates[i].month = month;
  dates[i].year = year;
  }
  FILE* pf = fopen("data\\statisticsMonths.txt", "w");
  Consume* psu = consumeCarry();
  Puncher* ppu = puncherCarry();
  Consume* cur = psu;
  Puncher* current = ppu;
  //做成哈希,还有这样做,构建的过程都要O(N^2)
  //这里是下机计费,所以很有可能上机次数多但是收益少
  fprintf(pf, "+-------+---------------+---------------+\n");
  fprintf(pf, "|%-7s|%15s|%15s|\n", "Month", "Hands-on times", "Total turnover");
  fprintf(pf, "+-------+---------------+---------------+\n");
  printf("+-------+--------+-------------+\n");
  printf("|%-7s|%8s|%13s|\n", "年月份", "上机次数", "月总营销额/元");
  printf("+-------+--------+-------------+\n");
  for (int i = 0; i < 12; i++) {
  cur = psu;
  current = ppu;
  Date d = dates[i];
  long long min = d.timestamp;
  long long max = min + months[judgeLeapYear(d.year)][d.month] * 86400;
  double sum = 0.0;
  int count = 0; //上机次数
  //消费与上机次数统计
  while (orderNumber != 0 && cur != NULL &&
    workNumber != 0 && current != NULL) {
    if (cur->timeNode < max && cur->timeNode >= min) {
    sum += cur->money;
    }
    if (current->timeNode < max && current->timeNode >= min) {
    count++;
    }
    cur = cur->next;
    current = current->next;
  }
  while (orderNumber != 0 && cur != NULL) {
    if (cur->timeNode < max && cur->timeNode >= min) {
    sum += cur->money;
    }
    cur = cur->next;
  }
  while (workNumber != 0 && current != NULL) {
    if (current->timeNode < max && current->timeNode >= min) {
    count++;
    }
    current = current->next;
  }
  fprintf(pf, "|%4d.%2d|%15d|%15.2lf|\n", d.year, d.month, count, sum);
  fprintf(pf, "+-------+---------------+---------------+\n");
  printf("|%4d.%2d|%8d|%13.2lf|\n", d.year, d.month, count, sum);
  printf("+-------+--------+-------------+\n");
  }
  fclose(pf);
  //free掉
  exitOutConsume(psu);
  exitOutPuncher(ppu);
}



5.5.1 引入 Date 年月结构体(basis.h)

//日期年月
typedef struct {
  int year;
  int month;
  long long timestamp;//首时间戳
}Date;

此年此月的00:00:00时的时间戳~

5.5.2 引入全局变量:润平年月份日数对应数组~

months(SearchStatistics.c)


int months[2][13] = { { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
  {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} };


平年 – 2月 28天~

闰年 – 2月 29天~

5.5.3 判断闰年函数以及判断年月函数~

judgeLeapYear(Searchstatistics.c)
judgeMonth(SearchStatistics.c)
int judgeLeapYear(int year) {
  return (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
}
Date judgeMonth(long long timeStamp) {
  long long times = 1672502400;
  int year = 2023;
  int month = 1;
  int i = 0;
  while ((times += months[judgeLeapYear(year)][month] * 86400) <= timeStamp) {
  i++;
  month = i % 12 + 1;
  if (month == 1) {
    year++;
  }
  }
  long long timestamp = times - months[judgeLeapYear(year)][month] * 86400;
  Date date = { year, month, timestamp};
  return date;
}


我这里是从2023年开始算起,因为现在不可能是2023年之前~


通过计算,2023-1-1 00:00:00 的时候的时间戳为1672502400~

这里用到返回结构体变量的技巧去返回两个值~


5.5.4 推导近12个月

建立一个12大的dates数组

最后一个元素便是judgeMonth(time(NULL))的返回值~

记录year,month

逆推出近12个月~

结合年份月份天数数组,计算对应月份的“首时间戳”~

dates[i].timestamp = dates[i + 1].timestamp - months[judgeLeapYear(year)][month] * 86400;

但month为0时,说明是翻到了前年~

应做出处理~

Date date = judgeMonth(time(NULL));
  int year = date.year;
  int month = date.month;
  Date dates[12];
  dates[11] = date;
  for (int i = 10; i >= 0; i--) {
  month--;
  if (month == 0) {
    year--;
    month = 12;
  }
  dates[i].timestamp = dates[i + 1].timestamp - months[judgeLeapYear(year)][month] * 86400;
  dates[i].month = month;
  dates[i].year = year;
  }


5.5.5 建立文本文档记录 + 打印到屏幕~

引入新文件 "data\\statisticsMonths.txt",建立文本文档~,用“w”

学校要求~

引入两个探路指针~

列表头写入文件中并且打印在屏幕上~

显示的跟记录的不一样的原因是:

C语言导出的文本文档中文格式不好看,不同文本编辑器打开都有可能不一样,可能乱码,得把文本编辑器编码方式改为ANSI~

并且很难对齐~

FILE* pf = fopen("data\\statisticsMonths.txt", "w");
  Consume* psu = consumeCarry();
  Puncher* ppu = puncherCarry();
  Consume* cur = psu;
  Puncher* current = ppu;
  //做成哈希,还有这样做,构建的过程都要O(N^2)
  //这里是下机计费,所以很有可能上机次数多但是收益少
  fprintf(pf, "+-------+---------------+---------------+\n");
  fprintf(pf, "|%-7s|%15s|%15s|\n", "Month", "Hands-on times", "Total turnover");
  fprintf(pf, "+-------+---------------+---------------+\n");
  printf("+-------+--------+-------------+\n");
  printf("|%-7s|%8s|%13s|\n", "年月份", "上机次数", "月总营销额/元");
  printf("+-------+--------+-------------+\n");


后续打印方式还是这样:

5.5.6 遍历12次打印表格~

for (int i = 0; i < 12; i++) {
  cur = psu; //回到一开始
  current = ppu; //回到一开始
  Date d = dates[i];
  long long min = d.timestamp;
  long long max = min + months[judgeLeapYear(d.year)][d.month] * 86400;
  double sum = 0.0;
  int count = 0; //上机次数
  //消费与上机次数统计
  while (orderNumber != 0 && cur != NULL &&
  workNumber != 0 && current != NULL) {
  if (cur->timeNode < max && cur->timeNode >= min) {
    sum += cur->money;
  }
  if (current->timeNode < max && current->timeNode >= min) {
    count++;
  }
  cur = cur->next;
  current = current->next;
  }
  while (orderNumber != 0 && cur != NULL) {
  if (cur->timeNode < max && cur->timeNode >= min) {
    sum += cur->money;
  }
  cur = cur->next;
  }
  while (workNumber != 0 && current != NULL) {
  if (current->timeNode < max && current->timeNode >= min) {
    count++;
  }
  current = current->next;
  }
  fprintf(pf, "|%4d.%2d|%15d|%15.2lf|\n", d.year, d.month, count, sum);
  fprintf(pf, "+-------+---------------+---------------+\n");
  printf("|%4d.%2d|%8d|%13.2lf|\n", d.year, d.month, count, sum);
  printf("+-------+--------+-------------+\n");
}
fclose(pf);
//free掉
exitOutConsume(psu);
exitOutPuncher(ppu);

引入两个局部变量,规定时间区间

min为此年月对应的首时间戳

max为下个月对应的首时间戳

时间区间为 [min, max) (左闭右开)

引入两个局部变量,记录

sum记录月营销额~

count记录月上机次数~

开始遍历,一开始两个链表一起遍历,当然是有一个先停下来的可能的

停下来后另一个继续跑

然后,将数据按对应格式写入到文件中 并且打印在屏幕上~

总共遍历个12次


最后,关闭文件(如果不关闭,那么数据就只会在程序的终结才能从缓存区导入文件!)


//释放空间
exitOutConsume(psu);
exitOutPuncher(ppu);


5.6 退出操作

超级简单地退出~

void exitOutStatistics(Card* pc) {
  printf("退出成功\n");
}

5.7 测试~

程序运行正常~




目录
相关文章
|
19天前
|
程序员 C语言 开发者
pymalloc 和系统的 malloc 有什么区别
pymalloc 和系统的 malloc 有什么区别
|
12天前
|
C语言 Windows
C语言课设项目之2048游戏源码
C语言课设项目之2048游戏源码,可作为课程设计项目参考,代码有详细的注释,另外编译可运行文件也已经打包,windows电脑双击即可运行效果
25 1
|
15天前
|
程序员 C语言 开发者
pymalloc 和系统的 malloc 有什么区别?
pymalloc 和系统的 malloc 有什么区别?
|
21天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
48 4
|
1月前
|
存储 缓存 C语言
C语言:链表和数组有什么区别
C语言中,链表和数组是两种常用的数据结构。数组是一种线性结构,元素在内存中连续存储,通过下标访问,适合随机访问且大小固定的情况。链表由一系列不连续的节点组成,每个节点存储数据和指向下一个节点的指针,适用于频繁插入和删除操作的场景,链表的大小可以动态变化。
|
1月前
|
C语言
无头链表再封装方式实现 (C语言描述)
如何在C语言中实现无头链表的再封装,包括创建节点和链表、插入和删除操作、查找和打印链表以及销毁链表的函数。
27 0
|
1月前
|
C语言
C语言链式结构之有头单链表再封装写法
本文介绍了如何使用C语言对有头单链表进行封装,包括节点的创建、链表的初始化、数据的插入和删除,以及链表的打印等功能。
17 1
|
1月前
|
C语言
C语言结构体链式结构之有头单链表
文章提供了一个C语言实现的有头单链表的完整代码,包括创建链表、插入、删除和打印等基本操作。
23 1
|
20天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
39 0
|
1月前
|
存储 编译器 C语言
【C语言】学生管理系统:完整模拟与实现(一)
【C语言】学生管理系统:完整模拟与实现