You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CPU信息存储链表程序:二次输入失败及内存泄漏问题排查

问题根源分析与修复方案

你的程序崩溃和内存泄漏问题不是gets_s()函数或指针参数的问题,而是链表逻辑错误、输入处理逻辑颠倒,以及内存释放不彻底导致的。下面逐个拆解问题并给出修复:

一、第二个条目输入崩溃/无法继续的原因

当你用fscanf输入数字(比如cpu_MHz、cache_size)时,fscanf只会读取数字,不会处理输入后的换行符。这些残留的换行符会留在stdin缓冲区中,当下一次调用gets_s()时,fgets()会直接读取到这个换行符,导致vendor_id被设为空字符串,触发循环退出;同时你的输入处理函数循环条件完全写反,导致输入验证失效,可能读取到非法值或陷入异常逻辑。

二、内存泄漏的原因

  1. 未释放结构体内部的字符串内存:每个cpu_specs结构体里的vendor_id和cpu_model都是malloc分配的,但你只释放了结构体本身,没释放这些字符串指针,导致间接泄漏。
  2. 漏掉最后一个节点的释放:你的释放循环for (current = head_to_cpu_stru; current -> next != NULL; ...)会跳过最后一个节点(因为最后一个节点的next是NULL,循环不执行),导致这个结构体内存泄漏。
  3. 循环退出时未释放最后一次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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 14:32:34