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,提问作者袁庭宁
相关产品推荐
相关产品推荐

