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

调用free后重新分配内存,char*为何输出垃圾值?

问题分析:分词程序释放内存后出现垃圾值的原因

问题重现

代码实现

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

void tokenize(char *buffer, char** tokens, int n) {
  int tokens_index = 0;
  int curr_index = 0;

  for(int i = 0; i < n; i++) {
    if(buffer[i] != ' ' && buffer[i] != '\0') {
      tokens[tokens_index][curr_index] = buffer[i];
      curr_index++;
    }
    else if(i != 0 && buffer[i] == ' ' && buffer[i-1] == ' ') {
      continue;
    }
    else if(buffer[i] == ' ') {
      curr_index = 0;
      tokens_index++;
    }
    else if(buffer[i] == '\0') {
      tokens_index++;
      tokens[tokens_index][0] == '\0';
      break;
    }
  }
}


int main(int argc, char **argv) {
  char buffer[50];

  while(fgets(buffer, 50, stdin)) {
    char **tokens = (char **) malloc(sizeof(char *) * 20);
    for(int i = 0; i < 20; i++) {
      tokens[i] = (char *) malloc(sizeof(char) * 50);
    }
    tokenize(buffer, tokens, 50);
    for(int i = 0; i < 20; i++) {
      if(*tokens[i] == '\0') {
        break;
      }
      printf("%s\n", tokens[i]);
    }

    for(int i = 0; i < 20; i++) {
      free(tokens[i]);
    }
    free(tokens);
  }

  return 0;
}

测试输入

ab cd ef
gh ij kl

异常输出

ab
cd
ef

gh
ij
kl
��
��
`�

问题核心:首次循环运行正常,后续循环释放内存后重新分配,输出出现垃圾值;移除free语句则运行正常。


问题根源

  1. 字符串未正确终止:tokenize函数仅向tokens中写入字符,但未给每个分词添加C字符串必需的终止符'\0'。首次运行时,malloc分配的堆内存可能恰好初始化为全0,未被赋值的tokens元素开头是'\0',打印循环会正常终止。但释放内存后再次malloc,堆内存会残留之前的垃圾数据,未终止的分词会带上这些垃圾,未被赋值的tokens元素也不是以'\0'开头,导致printf输出乱码。

  2. 赋值逻辑错误:在处理'\0'的分支中,代码写成了tokens[tokens_index][0] == '\0';,这是比较操作而非赋值,完全起不到标记结束的作用。


修复方案

修正后的tokenize函数

void tokenize(char *buffer, char** tokens, int n) {
  int tokens_index = 0;
  int curr_index = 0;

  // 初始化第一个token的终止符,处理空输入边界
  tokens[tokens_index][0] = '\0';

  for(int i = 0; i < n; i++) {
    if(buffer[i] != ' ' && buffer[i] != '\0') {
      tokens[tokens_index][curr_index] = buffer[i];
      curr_index++;
      // 实时添加终止符,确保字符串合法
      tokens[tokens_index][curr_index] = '\0';
      // 防止单个分词超出内存限制
      if(curr_index >= 49) break;
    }
    else if(i != 0 && buffer[i] == ' ' && buffer[i-1] == ' ') {
      continue;
    }
    else if(buffer[i] == ' ') {
      // 给当前分词添加终止符
      tokens[tokens_index][curr_index] = '\0';
      curr_index = 0;
      tokens_index++;
      // 初始化下一个分词的终止符
      tokens[tokens_index][0] = '\0';
    }
    else if(buffer[i] == '\0') {
      // 给最后一个分词添加终止符
      tokens[tokens_index][curr_index] = '\0';
      tokens_index++;
      // 标记结束位置
      tokens[tokens_index][0] = '\0';
      break;
    }
  }
}

关键修复点

  • 每个分词在写入字符后立即添加'\0',确保是合法的C字符串;
  • 切换分词时,先给当前分词加终止符,再初始化下一个分词的终止符;
  • 修正了==为=的赋值错误;
  • 增加了单个分词的长度限制,防止数组越界。

额外优化建议

  • 无需预先分配20个分词,可以根据实际分词数量动态分配内存,减少浪费;
  • 使用标准库函数strtok或strsep可以简化分词逻辑,避免手动实现的潜在bug。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 14:31:51