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

C语言malloc分配char指针内存时如何正确调用free()避免报错

故障原因

代码触发运行时内存错误是多类非法内存操作叠加导致的,具体问题点如下:

  • 数组越界写:给pid_array元素做地址偏移的循环从i=0启动,第一次循环就访问了pid_array[-1],属于数组越界写入,直接破坏了malloc维护的堆内存元数据,是最早的内存损坏点。正确逻辑应该从i=1开始偏移,因为pid_array[0]已经指向连续内存块的首地址。
  • 非法free非malloc首地址:你采用的是「二级指针数组+单块连续字符串存储区」的分配模式,整块字符串存储区仅需要free一次首地址pid_array[0]即可,但代码写了循环对0~MAX_LEN-1所有pid_array[i]执行free,除pid_array[0]外其余指针都是指向同一块malloc内存的内部偏移位置,不是malloc返回的独立内存块首地址,对这类地址执行free属于未定义行为,直接触发堆损坏。
  • 释放后使用+野指针free:循环free完所有pid_array[i]后,代码又调用getFileName将pid_array元素指向readdir返回的d_name地址(该地址属于目录流内部维护的内存,不是你自行分配的堆地址),后续再执行free(pid_array[0])时,传入的已经不是之前malloc返回的地址,free非堆地址直接触发崩溃。
  • free顺序错误:就算没有前面的指针覆写问题,代码先free(pid_array)释放二级指针数组本身,之后再访问pid_array[0]属于释放后使用(UAF),正确顺序应该先释放指向的内容块,再释放二级指针数组本身。
  • 业务逻辑写反:isPid函数判断逻辑反转,遇到非数字字符返回1、全数字返回0,会把非PID的目录名误识别为PID收集。

报错信息翻译

你遇到的是glibc堆内存校验失败的典型错误,对应中文含义:

*** 检测到程序错误 ***:free() 传入无效指针/重复释放
已放弃(核心已转储)

修复方案

核心修正逻辑:

  • 偏移赋值循环从i=1起始,避免越界访问
  • 连续内存分配模式下,仅free一次连续内存块首地址,禁止循环free内部偏移指针
  • 内存使用完成后再执行释放,遵循「子块先释放、父块后释放」的顺序
  • 收集文件名时用strcpy将内容拷贝到自行分配的内存中,不要直接存储d_name的临时指针
  • 修正isPid的判断返回值逻辑

修复后可运行代码:

#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MAX_LEN 1000
#define SINGLE_LEN 10

int isPid(char *str) {
  int len = strlen(str);
  for (int i = 0; i < len; i++) {
    if (isdigit(str[i]) == 0) {
      return 0;
    }
  }
  return 1;
}
void getFileName(char *dir_path, char *pid_array[], int *len) {
  DIR *dir = opendir(dir_path);
  if (dir == NULL) {
    fprintf(stderr, "path open failed!\n");
    exit(EXIT_FAILURE);
  }
  struct dirent *ent;
  int i = 0;
  while ((ent = readdir(dir)) != NULL && i < MAX_LEN) {
    if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
      continue;
    }
    if (isPid(ent->d_name) == 1) {
      strncpy(pid_array[i], ent->d_name, SINGLE_LEN - 1);
      pid_array[i][SINGLE_LEN - 1] = '\0';
      i++;
    }
  }
  *len = i;
  closedir(dir);
}

int main(int argc, char *argv[]) {
  int pflag, nflag, vflag;
  pflag = 0;
  nflag = 0;
  vflag = 0;
  int opt;
  while ((opt = getopt(argc, argv, "pvn")) != -1) {
    switch (opt) {
      case 'p':
        pflag = 1;
        break;
      case 'v':
        vflag = 1;
        break;
      case 'n':
        nflag = 1;
        break;
    }
  }
  printf("pflag=%d; nflag=%d; vflag=%d; optind=%d\n", pflag, nflag, vflag, optind);
  char **pid_array = (char **)malloc(sizeof(char *) * MAX_LEN);
  assert(pid_array != NULL);

  pid_array[0] = (char *)malloc(sizeof(char) * SINGLE_LEN * MAX_LEN);
  assert(pid_array[0] != NULL);
  for(int i = 1; i < MAX_LEN; i++){
     pid_array[i] = pid_array[i-1] + SINGLE_LEN;
  }

  int *pid_array_len = (int *)malloc(sizeof(int));
  assert(pid_array_len != NULL);
  getFileName("/proc", pid_array, pid_array_len);
  for (int i = 0; i < *pid_array_len; i++) {
    printf("%d\n", atoi(pid_array[i]));
  }

  free(pid_array[0]);
  free(pid_array);
  free(pid_array_len);
  return 0;
}

内容的提问来源于stack exchange,提问作者袁庭宁

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 20:24:09