使用read和realloc读取未知大小文件时报realloc无效指针错误
错误原因分析
第一次报错 realloc(): invalid pointer
realloc要求传入的指针必须是malloc/calloc/realloc返回的堆内存起始地址,你最初的实现里每次读完都执行buf += curr_read_size,把起始指针偏移了,传入偏移后的指针不符合realloc的调用要求,直接触发错误。
第二次报错 realloc(): invalid next size
修改后的代码存在两个核心问题,直接导致堆内存越界、破坏了堆的管理元数据:
- 读操作越界:每次调用
read时第三个参数直接传了整个缓冲区的大小buf_size,但此时缓冲区里已经存了tot_read字节的内容,剩余可用空间只有buf_size - tot_read,你让read读超过剩余空间的长度,就会往缓冲区外写数据,破坏堆结构。 - 扩容判断逻辑错误:你用
curr_read_size >= buf_size作为扩容条件完全不对,curr_read_size是本次实际读到的字节数,等你读到的数据已经触发这个条件时,越界写入已经发生,堆结构早就被破坏了。
正确实现方案
核心逻辑是:每次读之前先检查剩余可用空间,不够就先扩容,再把数据读到缓冲区的末尾,全程不修改原始堆指针的起始地址。
这里给出修复后的代码,同时增加了输出参数返回读取到的总字节数,避免调用者不知道缓冲区有效内容长度:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> // 参数size_out用于输出读取到的总字节数,不需要可以删掉 unsigned char* read_unknown_size(int fd, size_t* size_out){ size_t buf_size = 1024; // 建议用2的幂次作为初始大小,内存分配效率更高 size_t tot_read = 0; ssize_t curr_read_size; // read返回值是ssize_t,不要强转成int避免溢出 unsigned char* buf = malloc(buf_size); if(!buf){ perror("malloc failed"); exit(EXIT_FAILURE); } while(1){ // 先算剩余可用空间,小于1/2总大小就先扩容,避免频繁调用realloc if(buf_size - tot_read < buf_size / 2){ size_t new_size = buf_size * 2; unsigned char* tmp = realloc(buf, new_size); if(!tmp){ perror("realloc failed"); free(buf); // 出现错误先释放已分配的内存,避免内存泄漏 exit(EXIT_FAILURE); } buf = tmp; buf_size = new_size; } // 只读剩余可用空间的长度,不会越界 curr_read_size = read(fd, buf + tot_read, buf_size - tot_read); if(curr_read_size == -1){ perror("read failed"); free(buf); exit(EXIT_FAILURE); } if(curr_read_size == 0){ break; // 读到文件末尾退出 } tot_read += curr_read_size; } // 如果是读文本内容,可以加下面这行给末尾加结束符,读二进制可以删掉 // buf[tot_read] = '\0'; if(size_out != NULL){ *size_out = tot_read; } return buf; }
额外注意点
- 调用完这个函数后,不要忘记手动
free返回的缓冲区,避免内存泄漏。 - 不要提前保存初始的
buf地址作为返回值,realloc可能会把整块内存迁移到新的地址,直接返回扩容后的buf即可。
内容的提问来源于stack exchange,提问作者RedYoel
相关产品推荐
相关产品推荐

