CPU信息存储链表程序:二次输入失败及内存泄漏问题排查
问题根源分析与修复方案
你的程序崩溃和内存泄漏问题不是gets_s()函数或指针参数的问题,而是链表逻辑错误、输入处理逻辑颠倒,以及内存释放不彻底导致的。下面逐个拆解问题并给出修复:
一、第二个条目输入崩溃/无法继续的原因
当你用fscanf输入数字(比如cpu_MHz、cache_size)时,fscanf只会读取数字,不会处理输入后的换行符。这些残留的换行符会留在stdin缓冲区中,当下一次调用gets_s()时,fgets()会直接读取到这个换行符,导致vendor_id被设为空字符串,触发循环退出;同时你的输入处理函数循环条件完全写反,导致输入验证失效,可能读取到非法值或陷入异常逻辑。
二、内存泄漏的原因
- 未释放结构体内部的字符串内存:每个
cpu_specs结构体里的vendor_id和cpu_model都是malloc分配的,但你只释放了结构体本身,没释放这些字符串指针,导致间接泄漏。 - 漏掉最后一个节点的释放:你的释放循环
for (current = head_to_cpu_stru; current -> next != NULL; ...)会跳过最后一个节点(因为最后一个节点的next是NULL,循环不执行),导致这个结构体内存泄漏。 - 循环退出时未释放最后一次
malloc的空节点:当你输入空行终止循环时,最后一次malloc的current结构体没有被使用,也没有被释放,导致直接泄漏。
三、具体修复步骤
1. 修复输入处理函数的循环条件与缓冲区问题
把循环条件的&&改成||(只要输入失败或不符合要求就重试),同时在读取完数字后清空缓冲区的换行,避免影响后续的字符串输入:
double input_double_number(void) { double decimal_number; _Bool input_check; while ((input_check = fscanf(stdin, "%lf", &decimal_number)) != 1 || fmod(decimal_number, 100.00) != 0) { if (input_check != 1) scanf("%*s"); // 清空非法输入内容 fprintf(stdout, "invalid input, enter this number again: "); } // 清空缓冲区残留的换行符 while (getchar() != '\n'); return decimal_number; } unsigned input_unsigned_number(void) { unsigned decimal_number; _Bool input_check; while ((input_check = fscanf(stdin, "%u", &decimal_number)) != 1 || decimal_number > 65536 || (decimal_number & (decimal_number - 1)) != 0) { if (input_check != 1) scanf("%*s"); fprintf(stdout, "invalid input, enter this number again: "); } // 清空缓冲区残留的换行符 while (getchar() != '\n'); return decimal_number; }
2. 修复链表循环与内存释放逻辑
- 输入循环退出后,释放最后一次
malloc的未使用空节点; - 释放链表时,先释放结构体内部的字符串指针,再释放结构体本身,确保遍历所有节点:
int main(void) { // ... 原有代码 ... // 循环退出后,释放最后一次malloc的未使用current节点 free(current); if (head_to_cpu_stru == NULL) puts("No any cpu specs.!"); else { puts("here are all cpu speces:"); puts("vendor_id\tcpu_model\tcpu_MHz\tcache_size\tclflush_size"); // 修复输出循环,遍历所有节点(包括最后一个) for (current = head_to_cpu_stru; current != NULL; current = current -> next) printf("%s\t%s\t%.2fMHz\t%uKB\t%uKB\n", current -> vendor_id, current -> cpu_model, current -> cpu_MHz, current -> cache_size, current -> clflush_size); } // 修复内存释放逻辑:逐个释放字符串和结构体 current = head_to_cpu_stru; while (current != NULL) { temp = current -> next; // 先保存下一个节点地址,避免释放后丢失 free(current -> vendor_id); free(current -> cpu_model); free(current); current = temp; } puts("program done!"); return 0; }
3. 修复输入循环中的cpu_model判断逻辑
原有的判断条件写反,调整为如果输入为空则终止流程并清理已分配的内存:
fputs("Input the cpu model(input sole [Enter] to terminate input): ", stdout); if (gets_s(&(current->cpu_model)) == NULL || current->cpu_model[0] == '\0') { // 清理已分配的vendor_id和当前结构体 free(current->vendor_id); free(current); // 如果是第一个节点,清空头指针 if (head_to_cpu_stru == current) head_to_cpu_stru = NULL; else previous->next = NULL; break; }
四、修复后的完整代码
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> unsigned input_unsigned_number(void); double input_double_number(void); char * gets_s(char ** ptr_to_ptr); struct cpu_specs { char * vendor_id; char * cpu_model; double cpu_MHz; unsigned cache_size: 16; unsigned clflush_size: 16; struct cpu_specs * next; }; int main(void) { struct cpu_specs * head_to_cpu_stru = NULL; struct cpu_specs * previous, * current, * temp; fputs("Input the vendor's id(input sole [Enter] to terminate input): ", stdout); current = (struct cpu_specs *)malloc(sizeof(struct cpu_specs)); if (current == NULL) { perror("malloc failed"); exit(EXIT_FAILURE); } while (gets_s(&(current->vendor_id)) != NULL && current->vendor_id[0] != '\0') { if (head_to_cpu_stru == NULL) head_to_cpu_stru = current; else previous->next = current; current->next = NULL; fputs("Input the cpu model(input sole [Enter] to terminate input): ", stdout); if (gets_s(&(current->cpu_model)) == NULL || current->cpu_model[0] == '\0') { free(current->vendor_id); free(current); if (head_to_cpu_stru == current) head_to_cpu_stru = NULL; else previous->next = NULL; break; } fputs("Input the cpu_MHz(cpu_MHz must be multiplier of 100): ", stdout); current->cpu_MHz = input_double_number(); fputs("Input the cache_size(cache_size<=65536 and cache_size must be power of 2): ", stdout); current->cache_size = input_unsigned_number(); fputs("Input the clflush_size(clflush_size<=65536 and clflush_size must be power of 2): ", stdout); current->clflush_size = input_unsigned_number(); temp = current; current = (struct cpu_specs *)malloc(sizeof(struct cpu_specs)); if (current == NULL) { perror("malloc failed"); exit(EXIT_FAILURE); } previous = temp; fputs("\nInput the vendor's id(input sole [Enter] to terminate input): ", stdout); } // 释放最后一次malloc的未使用节点 free(current); if (head_to_cpu_stru == NULL) puts("No any cpu specs.!"); else { puts("\nhere are all cpu speces:"); puts("vendor_id\tcpu_model\tcpu_MHz\tcache_size\tclflush_size"); for (current = head_to_cpu_stru; current != NULL; current = current->next) printf("%s\t%s\t%.2fMHz\t%uKB\t%uKB\n", current->vendor_id, current->cpu_model, current->cpu_MHz, current->cache_size, current->clflush_size); } // 释放所有节点的字符串和结构体 current = head_to_cpu_stru; while (current != NULL) { temp = current->next; free(current->vendor_id); free(current->cpu_model); free(current); current = temp; } puts("\nprogram done!"); return 0; } double input_double_number(void) { double decimal_number; _Bool input_check; while ((input_check = fscanf(stdin, "%lf", &decimal_number)) != 1 || fmod(decimal_number, 100.00) != 0) { if (input_check != 1) scanf("%*s"); fprintf(stdout, "invalid input, enter this number again: "); } while (getchar() != '\n'); return decimal_number; } unsigned input_unsigned_number(void) { unsigned decimal_number; _Bool input_check; while ((input_check = fscanf(stdin, "%u", &decimal_number)) != 1 || decimal_number > 65536 || (decimal_number & (decimal_number - 1)) != 0) { if (input_check != 1) scanf("%*s"); fprintf(stdout, "invalid input, enter this number again: "); } while (getchar() != '\n'); return decimal_number; } char * gets_s(char ** ptr_to_ptr) { unsigned i; char MAX_input[1024]; if (fgets(MAX_input, 1024, stdin)) { // 去掉换行符 for (i = 0; MAX_input[i] != '\n' && MAX_input[i] != '\0' && i < 1024; i++); if (MAX_input[i] == '\n') MAX_input[i] = '\0'; *ptr_to_ptr = (char *)malloc(strlen(MAX_input) + 1); if (*ptr_to_ptr == NULL) { perror("fail to allocation"); exit(EXIT_FAILURE); } strcpy(*ptr_to_ptr, MAX_input); return *ptr_to_ptr; } else { return NULL; } }
验证修复效果
- 现在可以正常输入多个CPU条目,不会出现崩溃或无法继续输入的问题;
- 用LeakSanitizer检测时,所有分配的内存(结构体和内部字符串)都被正确释放,内存泄漏完全消失。
内容的提问来源于stack exchange,提问作者brushmonk
相关产品推荐
相关产品推荐

