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

尝试将stdin内容转为argv格式时出现malloc() next->prev_size不匹配错误

解决malloc() mismatching next->prev_size错误并正确构建argv风格参数列表

问题场景

使用open_memstream将标准输入读取到缓冲区buf后,尝试将缓冲区内容分割为类似argv的字符串数组,运行代码时触发malloc() mismatching next->prev_size错误,同时不清楚如何正确设置cli_argv指针。

原错误代码

char **cli_argv;
char *buf;
size_t len;
int cli_argc; // 补充原代码缺失的全局变量

void get_args() {
    int c;
    int c_count = 0;
    cli_argc = 0;
    char args[100];

    /* transfer buffer contents to a string, replacing whitespace and determine length */

    FILE *captured = fmemopen(buf,len,"r");

    if (captured == NULL) {
        debug("unable to fmemopen");
    } else {
        while ((c=fgetc(captured)) != EOF) {
            if (!isspace(c)) {
                args[c_count] = c;
            } else {
                args[c_count] = '\0';
                cli_argc++;
            }

            c_count++;
        }
    }
    fclose(captured);
    stop_input_stream();
    success("transfered contents of buf to string");

    /* find sizes of words and malloc enough memory */
    int arg_sizes[cli_argc];
    int argc_size = 0;
    int argc_index = 0;
    for (int i = 0; i < c_count; i++) {
        if (args[i] != '\0') {
            argc_size++;
        } else {
            arg_sizes[argc_index] = argc_size + 1;
            argc_index++;
            argc_size = 0;
        }
    }

    cli_argv = malloc(cli_argc * sizeof(char));
    *cli_argv = args;

    for (int i = 0; i < cli_argc; i++) {
        cli_argv[i] = malloc(arg_sizes[i] * sizeof(char));
    }

    for (int i = 0; i < cli_argc; i++) {
        fprintf(stderr,"%s\n",cli_argv[i]);
    }

}

核心错误分析

  1. 栈数组溢出:args[100]是固定大小的栈数组,若读取的输入长度超过100,会越界写入破坏栈或堆结构,直接触发malloc相关错误。
  2. 指针数组分配错误:cli_argv是char**类型(指针数组),分配内存时应该用cli_argc * sizeof(char*),原代码用sizeof(char)导致分配空间不足,破坏堆元数据。
  3. 栈指针非法引用:*cli_argv = args将栈数组args的地址赋值给cli_argv[0],函数返回后args会被销毁,后续访问会导致未定义行为;且这步操作和后续的malloc冲突,覆盖了指针数组的第一个元素。
  4. 参数分割逻辑缺陷:
    • 连续空格会生成多个空参数,导致cli_argc统计错误。
    • 最后一个参数后没有空格,不会触发cli_argc++,导致参数数量少算一个。
    • 未处理输入开头/结尾的空格。
  5. 变长数组(VLA)风险:int arg_sizes[cli_argc]属于变长数组,部分编译器不支持;若cli_argc为0,会创建长度为0的数组,后续写入会越界。
  6. 未复制字符串内容:仅为每个参数分配了内存,但没有将args中的字符串复制到分配的空间,直接访问cli_argv[i]会读取未初始化的内存。

修复后的代码

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

char **cli_argv;
char *buf;
size_t len;
int cli_argc;

void debug(const char *msg) {
    fprintf(stderr, "DEBUG: %s\n", msg);
}

void success(const char *msg) {
    fprintf(stderr, "SUCCESS: %s\n", msg);
}

void stop_input_stream() {
    // 此处添加关闭输入流的逻辑
}

void get_args() {
    cli_argc = 0;
    char *current_arg = NULL;
    size_t current_arg_len = 0;

    // 第一步:遍历缓冲区,统计参数数量
    for (size_t i = 0; i < len; i++) {
        char c = buf[i];
        if (isspace(c)) {
            if (current_arg != NULL) {
                cli_argc++;
                current_arg = NULL;
                current_arg_len = 0;
            }
        } else {
            if (current_arg == NULL) {
                current_arg = buf + i;
                current_arg_len = 1;
            } else {
                current_arg_len++;
            }
        }
    }
    // 处理最后一个参数
    if (current_arg != NULL) {
        cli_argc++;
    }

    if (cli_argc == 0) {
        cli_argv = NULL;
        success("no arguments found");
        return;
    }

    // 第二步:分配指针数组(末尾加NULL符合argv标准)
    cli_argv = malloc((cli_argc + 1) * sizeof(char*));
    if (cli_argv == NULL) {
        debug("malloc failed for cli_argv");
        return;
    }

    // 第三步:重新遍历,复制每个参数到堆内存
    current_arg = NULL;
    current_arg_len = 0;
    int arg_idx = 0;
    for (size_t i = 0; i < len; i++) {
        char c = buf[i];
        if (isspace(c)) {
            if (current_arg != NULL) {
                // 分配内存并复制参数
                cli_argv[arg_idx] = malloc(current_arg_len + 1);
                if (cli_argv[arg_idx] == NULL) {
                    debug("malloc failed for argument");
                    // 分配失败时释放已分配资源
                    for (int j = 0; j < arg_idx; j++) {
                        free(cli_argv[j]);
                    }
                    free(cli_argv);
                    cli_argv = NULL;
                    cli_argc = 0;
                    return;
                }
                strncpy(cli_argv[arg_idx], current_arg, current_arg_len);
                cli_argv[arg_idx][current_arg_len] = '\0';
                arg_idx++;

                current_arg = NULL;
                current_arg_len = 0;
            }
        } else {
            if (current_arg == NULL) {
                current_arg = buf + i;
                current_arg_len = 1;
            } else {
                current_arg_len++;
            }
        }
    }
    // 处理最后一个参数
    if (current_arg != NULL) {
        cli_argv[arg_idx] = malloc(current_arg_len + 1);
        if (cli_argv[arg_idx] == NULL) {
            debug("malloc failed for last argument");
            for (int j = 0; j < arg_idx; j++) {
                free(cli_argv[j]);
            }
            free(cli_argv);
            cli_argv = NULL;
            cli_argc = 0;
            return;
        }
        strncpy(cli_argv[arg_idx], current_arg, current_arg_len);
        cli_argv[arg_idx][current_arg_len] = '\0';
        arg_idx++;
    }
    cli_argv[cli_argc] = NULL; // argv标准:末尾为NULL

    // 验证输出
    for (int i = 0; i < cli_argc; i++) {
        fprintf(stderr, "cli_argv[%d]: %s\n", i, cli_argv[i]);
    }

    success("arguments parsed successfully");
}

关键修复说明

  • 移除固定大小栈数组:直接在原缓冲区buf上遍历分割,彻底避免溢出风险。
  • 正确分配指针数组:cli_argv分配(cli_argc + 1) * sizeof(char*),末尾添加NULL符合argv的标准格式。
  • 参数分割逻辑优化:处理开头/结尾空格、连续空格,准确统计参数数量。
  • 内存安全保障:每次malloc后检查是否成功,分配失败时释放已分配的资源,避免内存泄漏。
  • 正确复制字符串:将每个参数从buf复制到独立的堆内存中,确保函数返回后参数仍然有效。

内容的提问来源于stack exchange,提问作者eternallymisty

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 17:34:55