10s让你知道如何释放结构体,打败double-free!

本文主要介绍了在C语言中如何正确释放结构体,强调在释放结构体前需先释放其包含的数据结构。通过示例解析了结构体定义中的常见误区,并提供了修正方法,帮助开发者理解如何避免double-free问题。

Authur Whywait 做一块努力吸收知识的海绵~

如果赶时间,直接向下滑动并看粗体字即可。

我们在进行做数据结构相关编程题的时候,常会遇到最后要释放结构体的情况。

这也是这部片文章需要说的内容。

文章从以下几方面入手,讲述结构体释放操作中几点值得注意的事情。

语言:C

free()

首先释放结构体,我们要使用到free()函数。

说到free()不得不提到malloc()。

关于malloc(),请点这里。

所以与其说是将如何释放结构体,不如说如何使用free释放结构体。

使用事项

  1. 释放结构体时,需要把结构体里面的数据结构释放之后,再释放结构体。

直接上例子:

typedef struct Stack{
    int* val;
    int valSize;
    int top;
} BSTIterator;

void bSTIteratorFree(BSTIterator* obj) {
    if(obj && obj->val) free(obj->val);
    if(obj) free(obj);
}

如果obj本身为NULL,就没必要释放了。

  1. 关于结构体定义

再看下面这个例子:

typedef struct Stack{
    int val[10000]
    int top;
} BSTIterator;

void bSTIteratorFree(BSTIterator* obj) {
    if(obj && obj->val) free(obj->val);
    if(obj) free(obj);
}

看似没问题,先释放结构体里面的数据结构然后释放结构体,其实是你对struct结构体的定义不是很理解。

看下图你可能就可以自己分析出原因了。

在这里插入图片描述
此为运行结果
在这里插入图片描述
说明了什么呢?

val[10000]为一静态数组。结构体就包括了整个数组。

而上文中提到的int* val;,说明结构体里面包括的只是一个指针,也就是存储int型数据的地址。

这就是区别。

怎么改呢?

typedef struct Stack{
    int val[10000]
    int top;
} BSTIterator;

void bSTIteratorFree(BSTIterator* obj) {
    if(obj) free(obj);
}

如此就可以将结构体释放干净。

总结

几个malloc()就配上几个free()

/* 1. 设计表示单个学生信息的数据结构; 2. 设计顺序表结构,用于存储一个班的学生数据; 3. 分析函数流程,弄清楚各函数实现功能; 4. 熟悉文件的操作,编写函数从文件读取学生信息; 5. 编写输出函数,熟悉格式控制符的使用; 6. 设计测试文件,测试程序逻辑和编写的函数; 7. 给出测试结果,分析程序性能。 二、实验内容 完成以下实验步骤,调试运行程序并完成报告。 1. 对每个教学班生成一个文本文件(以教学班编号命名),包括班上的学 生成绩。一行一条学生信息的记录,不同属性用空格隔开, 输入数据中 无总成绩的值。 2. 设计表示学生信息的结构 struct Student;设每个学生包含如下信息: 属性 名 姓名 学号 实验成 绩(20%) 半期成绩 (30%) 期末成绩 (30%) 项目成绩 (20%) 总评成绩 四舍五入 类型 字符串 字符串 整数 整数 整数 整数 整数 3. 设计一个顺序表结构,struct TableScore 表示教学班或行政班的成绩表,每个 教学班或行政班学生人数不超过 50 人,教学班没有班号,但有任课教师姓 名,行政班有班号,但没有任课教师名字;没有的字段可以用 NULL 表示;一 个教学班中最多 20 个行政班。 4. 编写函数从文件读入一个教学班的学生成绩信息,并保存到一个顺序表中: a) 函数定义 int ReadStudentInfo(const char* filename, struct TableScore * pTableSc); 函数返回值:建立成功返回 1,失败返回 0。输入参数 const char* filename:教学班数据文件名;struct TableScore * pTableSc:待建 立的顺序表。 b) 建议函数流程: i. 打开数据文件,失败返回 0; ii. 循环从文件中读取一个学生数据(一行一个学生数据),计算总 成绩,并保存到顺序表中;每读取一个学生的数据,人数加 1; 到文件尾时停止; iii. 关闭文件; iv. 返回 1。 c) 数据文件格式说明: 一行一条学生信息的记录,不同属性用空格隔开,输入数据中无总成 绩记录。 2. 编写统计函数: a) 函数定义 void Count(struct TableScore * pTableSc)。输入参数为保存教 学班学生数据的顺序表。 b) 功能为:统计每个教学班中行政班(根据学号判断行政班级)的平均 成绩;统计信息包括在一个教学班中,行政班的数量,每个行政班的 总人数、每个行政班的平均成绩,用全局变量来表示这些统计信息。 访问每个学生信息的时候,用指针来指向当前正在处理的学生。 c) 设计一个宏,实现根据学号计算班级,并在函数 Count 中使用。 3. 编写信息输出函数: 要求通过一定的格式控制符,使得输出美观。输出格式为: 学生信息: ----------------------------------------------------- 序号 姓 名 学 号 班级 实验成绩 半期成绩 期末成绩 项目成绩 总成绩 1 张三 2 …… 统计信息: ----------------------------------------------------- 序号 班号 学生人数 平均成绩 1 1 班 32 83 2 …… 4. 编写 main 函数实现: a) 定义变量,其中保存学生信息的数据文件直接在源代码中初始化; b) 读取学生信息,如果失败,打印失败信息并退出程序; c) 统计学生信息; d) 输出统计信息。 e) 如果需要,请释放内存。 参考文件:(文本文件,如 2023CS101.txt,用 ANSI 编码或其它编码,与编译器配 合无乱码输出为准) 张三 202301001 85 78 92 88 李四 202301002 92 85 79 91 王五 202302001 78 82 88 75 赵六 202302002 88 90 85 92 钱七 202301003 76 85 90 82 程序输出: 学生信息: ----------------------------------------------------- 序号 姓名 学号 班级 实验成绩 半期成绩 期末成绩 项目成绩 总成绩 1 张三 202301001 202301 85 78 92 88 86 2 李四 202301002 202301 92 85 79 91 86 3 王五 202302001 202302 78 82 88 75 82 4 赵六 202302002 202302 88 90 85 92 89 5 钱七 202301003 202301 76 85 90 82 84 统计信息: ----------------------------------------------------- 序号 班号 学生人数 平均成绩 1 202301 3 85.3 2 202302 2 85.5*/ #include <stdio.h> #include <stdlib.h> #include <string.h> //设计一个宏,实现根据学号计算班级,并在函数 Count 中使用。 #define Get_class(ID,class){\ if(strlen(ID)>=6){\ strncpy(class, ID, 6);\ class[6] = '\0';\ }\ else {\ printf("error\n根据学号计算班级失败,传入的ID长度有误\n");\ }\ } typedef struct { char name[20]; char ID[10]; int experiment_score; int midterm_score; int finaltern_score; int project_score; int total_score;//四舍五入后得整数 char Class[7];//所在班级 }Student; typedef struct { Student** p; int capacity; int size; }TableScore; struct classstat { char class_no[7]; // 行政班号 int student_count = 0; // 学生人数 double average_score = 0; // 平均成绩 }; typedef classstat ClassStat; ClassStat Class[20];//最多20个班级 //函数声明 int InitList(TableScore* q, int capacity);//顺序表初始化函数,返回-1表示操作失败 int DestroyTableScore(TableScore* t);//销毁顺序表结构体释放内存,返回-1表示操作失败 void Count( TableScore* pTableSc);//统计函数 /*输入参数为保存教学班学生数据的顺序表 功能为:统计每个教学班中行政班(根据学号判断行政班级)的平均 成绩;统计信息包括在一个教学班中,行政班的数量,每个行政班的 总人数、每个行政班的平均成绩,用全局变量来表示这些统计信息。 访问每个学生信息的时候,用指针来指向当前正在处理的学生。*/ int Open_file(const char * file_name,FILE * p);//打开文件函数,传入文件名,该指针从此指向该文件,返回-1表示操作失败 int Read_file(FILE* p, TableScore*t);//读取文件信息并传入空白顺序表 int Print_student(TableScore* t);//打印学生信息 int Print_ClassStat(ClassStat* t);//打印统计表信息 int num_class = 0;//总的行政班的数量 int main() { TableScore a; TableScore *ap=&a; FILE* fp; InitList(ap, 20);//顺序表初始化函数,返回-1表示操作失败 Open_file("file_student",fp);//打开文件函数,传入文件名,该指针从此指向该文件,返回-1表示操作失败 Read_file(fp, ap);//读取文件信息并传入空白顺序表 Count(ap);//统计函数 Print_student(ap);//打印学生信息 Print_ClassStat(Class);//打印统计表信息 DestroyTableScore(ap);//销毁顺序表结构体释放内存,返回-1表示操作失败 } //顺序表初始化函数,返回1代表初始化成功,0代表失败 int InitList(TableScore * q, int Initcapacity) { q->capacity = Initcapacity; q->size = 0; q->p = (Student**)malloc(q->capacity * sizeof(Student*)); if (q->p == NULL) { printf("error!\n顺序表初始化失败\n"); return 0; } for (int cnt = 0; cnt < q->capacity; cnt++) { q->p[cnt] = NULL; } return 1; } //销毁顺序表结构体释放内存,返回1代表初始化成功,0代表失败 int DestroyTableScore(TableScore* t) { if (t == NULL) { printf("error!\n销毁时传入的指针为NULL\n"); } for (int cnt = 0; cnt < t->size; cnt++) { if (t->p[cnt] != NULL) { free(t->p[cnt]); t->p[cnt] = NULL;//防止野指针 } } free(t->p); t->capacity = 0; t->size = 0; return 1; } //打开文件函数,返回-1表示失败 int Open_file(const char * file_name ,FILE * p) { p = fopen(file_name, "r"); if (p == NULL) { printf("error!\n打开文件失败\n"); return -1; } return 1; } //读取文件信息并传入空白顺序表 int Read_file(FILE* p, TableScore* t) { if (p == NULL) { printf("error!\n读取信息时传入的文件指针为空\n"); return -1; } if (t == NULL) { printf("error!\n读取信息时传入的顺序表指针为空\n将根据信息临时初始化顺序表\n"); InitList(t, 20); } char line[256]; for (int cnt = 0; cnt < t->capacity; cnt++) { t->p[cnt] = (Student*)malloc(sizeof(Student));//初始化学生信息结构体 fgets(line, sizeof(line), p); sscanf(line, " %s %s %d %d %d %d", t->p[cnt]->name, t->p[cnt]->ID, &(t->p[cnt]->experiment_score), &(t->p[cnt]->midterm_score), &(t->p[cnt]->finaltern_score), &(t->p[cnt]->project_score) ); t->p[cnt]->total_score = (t->p[cnt]->experiment_score*0.2 + t->p[cnt]->midterm_score*0.3 + t->p[cnt]->finaltern_score*0.3 + t->p[cnt]->project_score*0.2) ; t->size++; } return 1; } //统计函数 /*输入参数为保存教学班学生数据的顺序表 功能为:统计每个教学班中行政班(根据学号判断行政班级,最多20个行政班)的平均 成绩;统计信息包括在一个教学班中,行政班的数量,每个行政班的 总人数、每个行政班的平均成绩,用全局变量来表示这些统计信息。 访问每个学生信息的时候,用指针来指向当前正在处理的学生。*/ void Count(TableScore* pTableSc) { //先遍历所有顺序表中的学生,确定行政班 for (int cnt = 0; cnt < pTableSc->size; cnt++) { Get_class(pTableSc->p[cnt]->ID, pTableSc->p[cnt]->Class); int found = 0; //遍历寻找所在的班级found用于判断是否找到 for (int i = 0,found=0; i < 20; i++) { if (pTableSc->p[cnt]->Class == Class[i].class_no) { found = 1; Class[i].student_count++; Class[i].average_score += pTableSc->p[cnt]->total_score; break; } } if (found == 0) { //创建新的行政班 if (num_class == 20) { printf("error/1\n行政班的数量超过上限\n"); return; } strncpy(Class[num_class].class_no, pTableSc->p[cnt]->Class,6); Class[num_class].student_count++; Class[num_class].average_score += pTableSc->p[cnt]->total_score; num_class++; } /*typedef struct { char class_no[7] = { 0 }; // 行政班号 int student_count; // 学生人数 double average_score; // 平均成绩 } ClassStat; ClassStat Class[20];//最多20个班级*/ } //统计每个行政班的平均分 for (int cnt = 0; cnt < num_class; cnt++) { Class[cnt].average_score /= Class[cnt].student_count; } } //打印学生信息 int Print_student(TableScore* t) { printf("学生信息:"); printf("----------------------------------------------------"); for (int cnt = 0; cnt < t->size; cnt++) { printf("%-4d %-2s %-4s %-10s %-7s %-3s %-3s %-3s %-3s %-3s", "序号", "姓名", "学号", "班级", "实验成绩", "半期成绩", "期末成绩", "项目成绩", "总成绩" ); printf("%-4d %-2s %-4s %-10s %-7d %-3d %-3d %-3d %-3d %-3d", cnt + 1, t->p[cnt]->name, t->p[cnt]->ID, t->p[cnt]->Class, t->p[cnt]->experiment_score, t->p[cnt]->midterm_score, t->p[cnt]->finaltern_score, t->p[cnt]->project_score, t->p[cnt]->total_score ); printf("\n"); } return 1; } //打印统计表信息 int Print_ClassStat(ClassStat* t) { printf("统计信息:"); printf("----------------------------------------------------"); printf("%-3s %-3s %-5s %-5s", "序号", "班号", "学生人数", "平均成绩" ); for (int cnt = 0; cnt < t->student_count; cnt++) { printf("%-2d %-3s %-5d %-5d", cnt+1, t[cnt].class_no, t[cnt].student_count, t[cnt].average_score ); } return 1; }
最新发布
12-13
#include <stdio.h> // 标准输入输出库 #include <stdlib.h> // 标准库函数,包含内存分配、系统命令等 #include <time.h> // 时间处理函数 #include <math.h> // 数学函数 #include <string.h> // 字符串处理函数 #include <ctype.h> // 字符处理函数 #define MAX_WINDOWS 10 // 最大窗口数量 #define VIP_WINDOW 0 // VIP窗口编号 #define VIP_NUMBER_BASE 1000 // VIP号码起始值 #define COMMON_NUMBER_BASE 2000 // 普通号码起始值 #define CLEAR_SCREEN system("cls") // 清屏命令 // 客户评价枚举类型 typedef enum { POOR=1, FAIR, GOOD, EXCELLENT } Rating; // 客户结构体 typedef struct { int number; // 客户号码 int isVip; // 是否是VIP客户 Rating rating; // 服务评价 time_t startTime, endTime; // 服务开始和结束时间 double duration; // 服务持续时间 } Customer; // 队列节点结构体 typedef struct queue { Customer customer; // 客户数据 struct queue* next; // 指向下一个节点的指针 } Queue, *QueuePtr; // 链式队列结构体 typedef struct { Queue *front, *tail; // 队列头和尾指针 } LinkQueue; // 银行窗口结构体 typedef struct { int windowId; // 窗口ID int isOpen; // 窗口是否开放 int servedCount; // 已服务客户数量 Customer serving; // 当前服务的客户 float avgRating; // 平均评分 double totalServiceTime; // 总服务时间 } BankWindow; // 银行系统结构体 typedef struct { LinkQueue commonQueue, vipQueue; // 普通和VIP队列 BankWindow windows[MAX_WINDOWS]; // 窗口数组 int windowCount; // 窗口总数 int nextVipNumber; // 下一个VIP号码 int nextCommonNumber; // 下一个普通号码 int isTakingNumbers; // 是否正在取号 } BankSystem; // 函数声明部分 int isEmptyQueue(const LinkQueue *Q); // 检查队列是否为空 void printDuration(double sec); // 打印时间间隔 void initBankSystem(BankSystem *bank, int windowCount); // 初始化银行系统 int enterQueue(BankSystem *bank, int isVip); // 客户进入队列 int deQueue(BankSystem *bank, int windowId); // 客户离开队列 int assignCustomerToWindow(BankSystem *bank); // 分配客户到窗口 int queryWaitingCount(const BankSystem *bank, int number); // 查询等待人数 void printStatus(const BankSystem *bank); // 打印系统状态 void printStats(const BankSystem *bank); // 打印统计信息 void adminMenu(BankSystem *bank); // 管理员菜单 void customerTakeNumber(BankSystem *bank); // 客户取号 void queryQueueStatus(BankSystem *bank); // 查询队列状态 void destroyBankSystem(BankSystem *bank); // 销毁银行系统 int getIntInput(const char *prompt, int min, int max); // 获取整数输入 void pressAnyKeyToContinue(); // 按任意键继续 // 检查队列是否为空 int isEmptyQueue(const LinkQueue *Q) { return Q->front == Q->tail; // 头尾指针相等表示队列为空 } // 打印时间间隔(秒转换为易读格式) void printDuration(double sec) { if (sec < 60) printf("%.0f秒", sec); // 小于1分钟显示秒 else if (sec < 3600) printf("%.0f分%.0f秒", sec/60, fmod(sec,60)); // 小于1小时显示分秒 else printf("%d小时%.0f分%.0f秒", (int)sec/3600, fmod(sec,3600)/60, fmod(sec,60)); // 大于1小时显示时分秒 } // 初始化银行系统 void initBankSystem(BankSystem *bank, int windowCount) { bank->windowCount = windowCount < MAX_WINDOWS ? windowCount : MAX_WINDOWS; // 设置窗口数量 bank->nextVipNumber = 1; // VIP号码从1开始 bank->nextCommonNumber = 1; // 普通号码从1开始 bank->isTakingNumbers = 1; // 默认允许取号 // 初始化普通队列 bank->commonQueue.front = bank->commonQueue.tail = (QueuePtr)malloc(sizeof(Queue)); bank->commonQueue.front->next = NULL; // 初始化VIP队列 bank->vipQueue.front = bank->vipQueue.tail = (QueuePtr)malloc(sizeof(Queue)); bank->vipQueue.front->next = NULL; // 初始化每个窗口 for (int i = 0; i < bank->windowCount; i++) { bank->windows[i] = (BankWindow){ .windowId = i, // 窗口ID .isOpen = 1, // 默认开放 .servedCount = 0, // 服务计数清零 .serving = { .number = -1 }, // 初始无客户 .avgRating = 0.0, // 平均评分初始0 .totalServiceTime = 0.0 // 总服务时间初始0 }; } } // 客户进入队列 int enterQueue(BankSystem *bank, int isVip) { if (!bank->isTakingNumbers) { // 检查是否允许取号 printf("当前已停止取号\n"); return 0; } QueuePtr newNode = (QueuePtr)malloc(sizeof(Queue)); // 创建新节点 if (!newNode) { // 内存分配失败处理 printf("内存分配失败\n"); return 0; } // 分配号码: VIP从1000开始,普通从2000开始 newNode->customer.number = isVip ? (VIP_NUMBER_BASE + bank->nextVipNumber++) : (COMMON_NUMBER_BASE + bank->nextCommonNumber++); newNode->customer.isVip = isVip; // 设置客户类型 newNode->customer.rating = 0; // 初始评价为0 newNode->next = NULL; // 新节点next指针置空 LinkQueue *q = isVip ? &bank->vipQueue : &bank->commonQueue; // 选择队列 q->tail->next = newNode; // 将新节点加入队列尾部 q->tail = newNode; // 更新尾指针 printf(isVip ? "VIP客户 %d 已进入VIP队列\n" : "客户 %d 已进入普通队列\n", newNode->customer.number); return 1; } // 客户离开队列(完成服务) int deQueue(BankSystem *bank, int windowId) { if (windowId < 0 || windowId >= bank->windowCount) { // 检查窗口ID有效性 printf("无效窗口编号\n"); return 0; } if (bank->windows[windowId].serving.number == -1) { // 检查窗口是否有客户 printf("窗口 %d 没有客户在服务\n", windowId); return 0; } Customer *c = &bank->windows[windowId].serving; // 获取当前客户 c->endTime = time(NULL); // 记录结束时间 c->duration = difftime(c->endTime, c->startTime); // 计算服务时长 // 获取评价 printf("\n请对窗口 %d 的服务进行评价(1-4):\n", windowId); printf("1. 差评\n2. 一般\n3. 好评\n4. 非常满意\n"); printf("请输入评价: "); int rating; while (1) { // 输入验证循环 if (scanf("%d", &rating) != 1 || rating < 1 || rating > 4) { printf("无效输入,请输入1-4的数字: "); while (getchar() != '\n'); // 清空输入缓冲区 continue; } break; } c->rating = rating; // 记录评价 BankWindow *w = &bank->windows[windowId]; // 获取窗口指针 // 更新平均评分 w->avgRating = (w->avgRating * w->servedCount + c->rating) / (w->servedCount + 1); w->totalServiceTime += c->duration; // 累加服务时间 w->servedCount++; // 增加服务计数 printf("\n窗口 %d: 客户 %d 已离开,服务时长: ", windowId, c->number); printDuration(c->duration); // 打印服务时长 printf(",服务评价: "); const char *ratings[] = {"", "差评", "一般", "好评", "非常满意"}; printf("%s\n", ratings[c->rating]); // 打印评价文字 c->number = -1; // 标记窗口为空闲 pressAnyKeyToContinue(); return 1; } // 分配客户到窗口 int assignCustomerToWindow(BankSystem *bank) { // 优先处理VIP窗口 if (bank->windows[VIP_WINDOW].isOpen && // VIP窗口开放 bank->windows[VIP_WINDOW].serving.number == -1 && // VIP窗口空闲 !isEmptyQueue(&bank->vipQueue)) { // VIP队列不为空 QueuePtr cust = bank->vipQueue.front->next; // 获取队列第一个VIP客户 bank->windows[VIP_WINDOW].serving = cust->customer; // 分配给VIP窗口 bank->windows[VIP_WINDOW].serving.startTime = time(NULL); // 记录开始时间 bank->vipQueue.front->next = cust->next; // 从队列中移除 if (bank->vipQueue.tail == cust) { // 如果是最后一个客户 bank->vipQueue.tail = bank->vipQueue.front; // 重置尾指针 } printf("VIP客户 %d 已分配到VIP窗口 %d\n", bank->windows[VIP_WINDOW].serving.number, VIP_WINDOW); free(cust); // 释放节点内存 return 1; } // 处理其他窗口 for (int i = 0; i < bank->windowCount; i++) { if (i == VIP_WINDOW) continue; // 跳过VIP窗口 BankWindow *w = &bank->windows[i]; // 获取窗口指针 if (w->isOpen && w->serving.number == -1) { // 窗口开放且空闲 // 优先分配VIP客户 if (!isEmptyQueue(&bank->vipQueue)) { QueuePtr cust = bank->vipQueue.front->next; w->serving = cust->customer; w->serving.startTime = time(NULL); bank->vipQueue.front->next = cust->next; if (bank->vipQueue.tail == cust) { bank->vipQueue.tail = bank->vipQueue.front; } printf("VIP客户 %d 已分配到窗口 %d\n", w->serving.number, i); free(cust); return 1; } // 分配普通客户 else if (!isEmptyQueue(&bank->commonQueue)) { QueuePtr cust = bank->commonQueue.front->next; w->serving = cust->customer; w->serving.startTime = time(NULL); bank->commonQueue.front->next = cust->next; if (bank->commonQueue.tail == cust) { bank->commonQueue.tail = bank->commonQueue.front; } printf("客户 %d 已分配到窗口 %d\n", w->serving.number, i); free(cust); return 1; } } } return 0; // 没有可分配的客户 } // 查询等待人数 int queryWaitingCount(const BankSystem *bank, int number) { // 判断客户类型 int isVip = (number >= VIP_NUMBER_BASE && number < COMMON_NUMBER_BASE); if (isVip) { // VIP客户查询 int count = 0; for (QueuePtr p = bank->vipQueue.front->next; p; p = p->next, count++) { if (p->customer.number == number) return count; // 返回前面的人数 } } else { // 普通客户查询 int count = 0; for (QueuePtr p = bank->commonQueue.front->next; p; p = p->next, count++) { if (p->customer.number == number) { // 普通客户前面还有所有VIP客户 int vipCount = 0; for (QueuePtr vip = bank->vipQueue.front->next; vip; vip = vip->next) { vipCount++; } return count + vipCount; // 返回VIP队列人数+普通队列前面人数 } } } // 检查是否正在服务 for (int i = 0; i < bank->windowCount; i++) { if (bank->windows[i].serving.number == number) return 0; // 正在服务,等待人数为0 } return -1; // 未找到该号码 } // 打印系统状态 void printStatus(const BankSystem *bank) { CLEAR_SCREEN; // 清屏 printf("\n==================== 银行排队系统 ====================\n"); // 当前排队情况 printf("\n【当前排队情况】\n"); printf("+------------+---------------------+\n"); printf("| 队列类型 | 排队号码 |\n"); printf("+------------+---------------------+\n"); printf("| VIP队列 | "); for (QueuePtr p = bank->vipQueue.front->next; p; p = p->next) { printf("%d ", p->customer.number); // 打印VIP队列号码 } printf("\n| 普通队列 | "); for (QueuePtr p = bank->commonQueue.front->next; p; p = p->next) { printf("%d ", p->customer.number); // 打印普通队列号码 } printf("\n+------------+---------------------+\n"); // 窗口服务状态 printf("\n【窗口服务状态】(窗口%d为VIP专用窗口)\n", VIP_WINDOW); printf("+--------+--------+--------+------------+----------------+------------+----------------+\n"); printf("| 窗口ID | 状态 | 客户号 | 客户类型 | 已服务时间 | 平均评分 | 平均服务时间 |\n"); printf("+--------+--------+--------+------------+----------------+------------+----------------+\n"); for (int i = 0; i < bank->windowCount; i++) { // 遍历所有窗口 BankWindow *w = &bank->windows[i]; printf("| %-6d | %-6s | ", w->windowId, w->isOpen ? "开放" : "关闭"); if (w->serving.number == -1) { // 窗口空闲 printf("%-6s | %-10s | %-12s | %-10.1f | %-14s |\n", "-", i == VIP_WINDOW ? "VIP专用" : "-", "-", w->avgRating, "-"); } else { // 窗口有客户 printf("%-6d | %-10s | ", w->serving.number, (w->serving.number >= VIP_NUMBER_BASE && w->serving.number < COMMON_NUMBER_BASE) ? "VIP" : "普通"); char timeStr[20] = "-"; if (w->serving.startTime) { // 计算已服务时间 sprintf(timeStr, "%.0f秒", difftime(time(NULL), w->serving.startTime)); } char avgTimeStr[20] = "-"; if (w->servedCount) { // 计算平均服务时间 sprintf(avgTimeStr, "%.0f秒", w->totalServiceTime / w->servedCount); } printf("%-14s | %-10.1f | %-14s |\n", timeStr, w->avgRating, avgTimeStr); } } printf("+--------+--------+--------+------------+----------------+------------+----------------+\n"); } // 打印统计信息 void printStats(const BankSystem *bank) { printf("\n【系统统计信息】\n"); printf("+------------------+--------+\n"); printf("| 项目 | 数值 |\n"); printf("+------------------+--------+\n"); printf("| 已发放VIP号码 | %-6d |\n", bank->nextVipNumber - 1); printf("| 已发放普通号码 | %-6d |\n", bank->nextCommonNumber - 1); int totalServed = 0, waiting = 0; double totalTime = 0; for (int i = 0; i < bank->windowCount; i++) { // 计算总服务人数和时间 totalServed += bank->windows[i].servedCount; totalTime += bank->windows[i].totalServiceTime; } // 计算等待人数 for (QueuePtr p = bank->commonQueue.front->next; p; p = p->next) waiting++; for (QueuePtr p = bank->vipQueue.front->next; p; p = p->next) waiting++; printf("| 已服务客户 | %-6d |\n", totalServed); printf("| 等待中客户 | %-6d |\n", waiting); if (totalServed) { // 如果有服务过的客户 char avgTime[20]; sprintf(avgTime, "%.0f秒", totalTime / totalServed); // 计算平均服务时间 printf("| 平均服务时间 | %-6s |\n", avgTime); // 计算平均评分 float totalRating = 0; for (int i = 0; i < bank->windowCount; i++) { totalRating += bank->windows[i].avgRating * bank->windows[i].servedCount; } printf("| 平均服务评分 | %-6.1f |\n", totalRating / totalServed); } printf("+------------------+--------+\n"); } // 管理员菜单 void adminMenu(BankSystem *bank) { while (1) { CLEAR_SCREEN; printStatus(bank); // 显示当前状态 printf("\n【管理员菜单】\n"); printf("1. 开放窗口\n"); printf("2. 关闭窗口\n"); printf("3. 开始取号\n"); printf("4. 停止取号\n"); printf("5. 查看统计信息\n"); printf("0. 返回主菜单\n"); printf("请选择操作: "); int choice = getIntInput("", 0, 5); // 获取选择 switch (choice) { case 1: { // 开放窗口 printf("输入要开放的窗口号(0-%d): ", bank->windowCount-1); int n = getIntInput("", 0, bank->windowCount-1); bank->windows[n].isOpen = 1; // 设置窗口为开放 printf("窗口 %d 已开放\n", n); assignCustomerToWindow(bank); // 尝试分配客户 break; } case 2: { // 关闭窗口 printf("输入要关闭的窗口号(0-%d): ", bank->windowCount-1); int n = getIntInput("", 0, bank->windowCount-1); if (bank->windows[n].serving.number != -1) { // 检查窗口是否有客户 printf("窗口 %d 正在服务客户,请先完成服务\n", n); } else { bank->windows[n].isOpen = 0; // 设置窗口为关闭 printf("窗口 %d 已关闭\n", n); } break; } case 3: // 开始取号 bank->isTakingNumbers = 1; printf("已开始取号\n"); break; case 4: // 停止取号 bank->isTakingNumbers = 0; printf("已停止取号\n"); break; case 5: // 查看统计信息 printStats(bank); pressAnyKeyToContinue(); break; case 0: // 返回主菜单 return; } pressAnyKeyToContinue(); } } // 客户取号 void customerTakeNumber(BankSystem *bank) { printf("\n【客户取号】\n"); printf("1. 普通客户取号\n"); printf("2. VIP客户取号\n"); printf("0. 返回\n"); printf("请选择: "); int choice = getIntInput("", 0, 2); // 获取选择 if (choice == 1 || choice == 2) { enterQueue(bank, choice-1); // 进入队列 assignCustomerToWindow(bank); // 尝试分配窗口 } pressAnyKeyToContinue(); } // 查询队列状态 void queryQueueStatus(BankSystem *bank) { CLEAR_SCREEN; printStatus(bank); // 显示当前状态 printf("\n要查询特定号码的等待人数吗? (1:是, 0:否): "); int choice = getIntInput("", 0, 1); // 获取选择 if (choice == 1) { printf("输入您的号码: "); int number; scanf("%d", &number); // 获取号码 int cnt = queryWaitingCount(bank, number); // 查询等待人数 if (cnt >= 0) { printf("号码 %d 前面还有 %d 人\n", number, cnt); } else { printf("号码 %d 未找到\n", number); } pressAnyKeyToContinue(); } } // 销毁银行系统(释放内存) void destroyBankSystem(BankSystem *bank) { // 释放普通队列内存 QueuePtr p; while (bank->commonQueue.front) { p = bank->commonQueue.front; bank->commonQueue.front = p->next; free(p); } // 释放VIP队列内存 while (bank->vipQueue.front) { p = bank->vipQueue.front; bank->vipQueue.front = p->next; free(p); } } // 获取整数输入(带验证) int getIntInput(const char *prompt, int min, int max) { int value; while (1) { printf("%s", prompt); // 显示提示 if (scanf("%d", &value) != 1) { // 读取输入 printf("输入无效,请输入数字\n"); while (getchar() != '\n'); // 清空输入缓冲区 continue; } if (value >= min && value <= max) { // 检查范围 return value; } printf("输入必须在%d到%d之间\n", min, max); } } // 按任意键继续 void pressAnyKeyToContinue() { printf("\n按任意键继续..."); while (getchar() != '\n'); // 清空输入缓冲区 getchar(); // 等待用户按键 } // 主函数 int main() { BankSystem bank; // 创建银行系统实例 printf("请输入银行窗口数量(1-%d): ", MAX_WINDOWS); int windowCount = getIntInput("", 1, MAX_WINDOWS); // 获取窗口数量 initBankSystem(&bank, windowCount); // 初始化系统 while (1) { // 主循环 CLEAR_SCREEN; printStatus(&bank); // 显示状态 printf("\n【主菜单】\n"); printf("1. 客户取号\n"); printf("2. 客户离开(完成服务)\n"); printf("3. 查询排队状态\n"); printf("4. 管理员菜单\n"); printf("0. 退出系统\n"); printf("请选择操作: "); int choice = getIntInput("", 0, 4); // 获取选择 switch (choice) { case 1: // 客户取号 customerTakeNumber(&bank); break; case 2: { // 客户离开 printf("输入离开窗口号(0-%d): ", bank.windowCount-1); int n = getIntInput("", 0, bank.windowCount-1); deQueue(&bank, n); // 处理客户离开 assignCustomerToWindow(&bank); // 尝试分配新客户 break; } case 3: // 查询状态 queryQueueStatus(&bank); break; case 4: // 管理员菜单 adminMenu(&bank); break; case 0: // 退出系统 destroyBankSystem(&bank); // 释放内存 printf("感谢使用,再见!\n"); exit(0); } } return 0; }评估代码
06-30
#include <limits.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <time.h> #include "floyd_spf.h" // Floyd-Warshall 算法计算所有节点对的最短路径 PathTableEntry* compute_all_shortest_paths(Graph* graph) { if (!graph || !graph->matrix) { return NULL; } graph_compact(graph); int n = graph->node_count; PathTableEntry* path_table = (PathTableEntry*)malloc(sizeof(PathTableEntry)); if (!path_table) { fprintf(stderr, "Memory allocation failed for path table\n"); return NULL; } path_table->node_count = n; path_table->table = (PathEntry**)malloc(n * sizeof(PathEntry*)); if (!path_table->table) { free(path_table); return NULL; } for (int i = 0; i < n; ++i) { path_table->table[i] = (PathEntry*)malloc(n * sizeof(PathEntry)); if (!path_table->table[i]) { for (int j = 0; j < i; ++j) { free(path_table->table[j]); } free(path_table->table); free(path_table); return NULL; } } // 初始化 for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { size_t weight = graph->matrix[i][j]; if (i == j) { path_table->table[i][j].cost = 0; strcpy(path_table->table[i][j].next_hop, get_ip(graph, i, 0)); } else if (weight != 0 && weight != INT_MAX) { // 注意这里判断是否为有效边 path_table->table[i][j].cost = weight; strcpy(path_table->table[i][j].next_hop, get_ip(graph, j, 0)); } else { path_table->table[i][j].cost = MAX_COST_VALUE; strcpy(path_table->table[i][j].next_hop, ""); } } } // Floyd-Warshall 算法 for (int k = 0; k < n; ++k) { for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { int new_cost = path_table->table[i][k].cost + path_table->table[k][j].cost; if (new_cost < path_table->table[i][j].cost) { path_table->table[i][j].cost = new_cost; strcpy(path_table->table[i][j].next_hop, path_table->table[i][k].next_hop); } } } } return path_table; } void print_path_table(Graph* graph,PathTableEntry* path_table) { if (!graph || !path_table || !path_table->table) { return; } int n = path_table->node_count; // 打印表头 printf("--------------------------------------------------------------------------------------------\n"); printf("Shortest Path Table (Node Count: %d)\n", n); printf("--------------------------------------------------------------------------------------------\n"); printf("%-10s", "From\\To"); for (int i = 0; i < n; ++i) { printf("%-15s", get_ip(graph, i , 0)); } printf("\n"); // 打印每一行 for (int i = 0; i < n; ++i) { printf("%-10s", get_ip(graph, i , 0)); // 当前行号 for (int j = 0; j < n; ++j) { if (path_table->table[i][j].cost == MAX_COST_VALUE) { printf("%-15s", "INF"); } else { char entry[64]; snprintf(entry, sizeof(entry), "%d (%s)", path_table->table[i][j].cost, path_table->table[i][j].next_hop); printf("%-15s", entry); } } printf("\n"); } printf("--------------------------------------------------------------------------------------------\n"); printf("Legend: Cell format is 'cost (next hop)', INF means no path exists.\n"); printf("--------------------------------------------------------------------------------------------\n"); } // 释放路径表内存 void free_path_table(PathTableEntry* path_table) { if (!path_table) { return; } if (path_table->table) { for (int i = 0; i < path_table->node_count; ++i) { free(path_table->table[i]); } free(path_table->table); } free(path_table); } // 获取最小距离节点的索引 int min_distance(int* dist, int* visited, int n) { int min = INT_MAX, min_index = -1; for (int v = 0; v < n; v++) { if (!visited[v] && dist[v] < min) { min = dist[v]; min_index = v; } } return min_index; } void generate_dense_graph(Graph* graph, int num_nodes) { // 添加num_nodes个节点 for (int i = 1; i <= num_nodes; i++) { char node_name[20]; snprintf(node_name, sizeof(node_name), "10.0.0.%d", i); graph_add_node(graph, node_name); } // 随机添加边:任意两个节点之间以概率p添加一条双向边(无向图) double probability = 1; // 边的概率,可以根据需要调整 for (int i = 1; i <= num_nodes; i++) { for (int j = i+1; j <= num_nodes; j++) { // 生成随机数决定是否添加边 if (rand() / (double)RAND_MAX < probability) { // 随机权重,1到10 int weight = rand() % 10 + 1; char node_i[20], node_j[20]; snprintf(node_i, sizeof(node_i), "10.0.0.%d", i); snprintf(node_j, sizeof(node_j), "10.0.0.%d", j); // 无向图,添加双向边 graph_add_edge(graph, node_i, node_j, weight); graph_add_edge(graph, node_j, node_i, weight); } } } } #include <time.h> #include <unistd.h> // 用于 usleep // 模拟参数(单位:毫秒) #define HELLO_INTERVAL 1000 // 每隔 1 秒发送一次 Hello #define LSA_FLOOD_DELAY 500 // LSA 泛洪延迟 #define SPF_TRIGGER_DELAY 300 // SPF 触发延迟 #define TOPOLOGY_SYNC_DELAY 200 // 拓扑同步延迟 // 生成 IP 地址字符串 const char* generate_ip(int index) { static char ip[32]; snprintf(ip, sizeof(ip), "10.0.0.%d", index); return ip; } // 模拟网络延迟(单位:毫秒) void simulate_network_delay(int ms) { usleep(ms * 1000); // usleep 接受微秒参数 } // 模拟节点初始化和 Hello 协议交互 void simulate_node_initialization(Graph* graph, int node_count) { printf("模拟节点初始化与 Hello 协议交互...\n"); for (int i = 0; i < node_count; i++) { char ip[32]; snprintf(ip, sizeof(ip), "10.0.0.%d", i); graph_add_node(graph, ip); simulate_network_delay(HELLO_INTERVAL / node_count); // 模拟 Hello 包发送间隔 } } // 模拟 LSA 泛洪 void simulate_lsa_flooding(Graph* graph, int node_count) { printf("模拟 LSA 泛洪...\n"); for (int i = 0; i < node_count; i++) { for (int j = 0; j < node_count; j++) { if (i != j) { int weight = rand() % 100 + 1; graph_add_edge(graph, get_ip(graph, i, 0), get_ip(graph, j , 0), weight); } } simulate_network_delay(LSA_FLOOD_DELAY / node_count); } } int main() { FILE* fp = fopen("results_with_simulated_delay.csv", "w"); if (!fp) { printf("无法创建文件 results_with_simulated_delay.csv\n"); return 1; } fprintf(fp, "Node Count,Floyd-Warshall (ms),Dijkstra (ms)\n"); for (int node_count = 100; node_count <= 2000; node_count += 100) { Graph* graph = graph_init(); // 1. 构建图(添加 node_count 个节点) generate_dense_graph(graph, node_count); // 2. 取图中第一个节点作为源节点 const char* src_ip = get_ip(graph, 0 , 0); // 确保源节点在图中 // 3. 测试 Floyd-Warshall clock_t start = clock(); PathTableEntry* floyd_table = compute_all_shortest_paths(graph); simulate_network_delay(TOPOLOGY_SYNC_DELAY); // 可选延迟 clock_t end = clock(); double floyd_time = (double)(end - start) * 1000 / CLOCKS_PER_SEC + 2000; // 4. 测试 Dijkstra(受模拟延迟影响) start = clock(); PathTableEntry* dijkstra_table = dijkstra(graph, src_ip); // print_path_table(dijkstra_table); // // 模拟 OSPF 延迟(不重复添加节点或边) // simulate_network_delay(TOPOLOGY_SYNC_DELAY); // 可选延迟 // simulate_lsa_flooding(graph, node_count); // 可选延迟 // simulate_node_initialization(graph, node_count) ; end = clock(); double dijkstra_time = (double)(end - start) * 1000 / CLOCKS_PER_SEC + node_count * 5000; // 5. 输出结果 printf("节点数:%d | Floyd-Warshall: %.2f ms | Dijkstra: %.2f ms\n", node_count, floyd_time, dijkstra_time); fprintf(fp, "%d,%.2f,%.2f\n", node_count, floyd_time, dijkstra_time); // 6. 释放资源 free_path_table(floyd_table); free_path_table(dijkstra_table); free_graph(graph); } fclose(fp); printf("结果已保存到 results_with_simulated_delay.csv\n"); return 0; }帮我实现一个dijkstra蒜贩,你只需要实现dijkstra的函数即可。
11-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AuthurLEE

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值