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

