C实现readLine跳出while循环报realloc invalid next size错误
问题根因
你代码里有4个确定性bug,直接导致所有异常现象:
- 循环逻辑运算符使用错误,造成永真循环
循环判断条件写为(c = getchar()) != '\n' || c != EOF,逻辑或||的规则是只要任意一个子条件为真,整体条件就为真。没有任何字符能同时等于'\n'和EOF,所以这个条件永远为真,循环永远不会主动退出。
读完输入的可见字符和换行符后,循环还会继续运行:要么持续等待新输入,要么在读到EOF后反复把EOF值写入缓冲区,很快越过malloc申请的内存边界写数据,破坏glibc malloc用于管理堆内存的元数据(chunk头),这就是realloc(): invalid next size错误的核心来源——堆结构已经被越界写损坏,后续任何内存分配/释放操作(包括printf内部隐含的堆操作)都会触发内存校验失败,直接abort进程。
你之前误以为不是死循环,是因为循环内加了位置打印能看到pos递增,实际上这就是越界写正在发生的表现。
- 循环逻辑运算符使用错误,造成永真循环
- 存储getchar返回值的变量类型错误
getchar()的返回值类型是int,合法返回值要么是0-255的无符号字符值,要么是EOF(通常为int类型的-1)。用char类型变量c存储返回值会发生类型截断:
- 如果运行环境下char是无符号类型,EOF(-1)会被转换为255,永远和int类型的EOF不相等,无法正确识别输入结束
- 如果运行环境下char是有符号类型,读到值为0xff的扩展ASCII字符时会被误判为EOF,提前截断输入
- 存储getchar返回值的变量类型错误
- 内存分配逻辑存在隐患
malloc申请内存时乘了sizeof(char),但realloc扩容时直接传了buffsize数值,虽然sizeof(char)标准规定为1不会造成当前逻辑的大小错误,但属于不规范写法;同时直接把realloc返回值赋值给原buffer指针,如果realloc分配失败返回NULL,原buffer的地址会丢失造成内存泄漏,也没有对realloc的返回值做合法性校验。
- 内存分配逻辑存在隐患
- 输出和字符串构造逻辑错误
printf("exited");没有加换行符,也没有手动fflush刷新标准输出,而stdout默认是行缓冲模式,没有遇到换行符时内容会暂存在缓冲区不输出到屏幕,哪怕代码执行到了这行你也看不到输出,误以为代码没走到- 读取完字符后没有在缓冲区末尾添加C字符串要求的终止符
'\0',返回的buffer不是合法C字符串,后续用字符串处理函数操作时会触发越界读。
修复后的完整代码
// 建议将shbuffsize设置为1024这类合理值 #define shbuffsize 1024 // 保持你之前实现的内存分配检查函数即可 int CheckMallocError(void *ptr, const char *desc); char *readLine(void){ int buffsize = shbuffsize; char *buffer = malloc(sizeof(char) * buffsize); // 修正c的类型为int,匹配getchar返回值 int c; int pos = 0; if(CheckMallocError(buffer, "buffer")){ exit(EXIT_FAILURE); } // 修正逻辑运算符为&&:读到换行符*或*读到EOF时退出循环 while ((c = getchar()) != '\n' && c != EOF) { // 预留1字节存最后的终止符'\0' if(pos >= buffsize - 1){ buffsize += shbuffsize; // 用临时指针存realloc返回值,避免分配失败丢失原指针 char *new_buf = realloc(buffer, sizeof(char) * buffsize); if(CheckMallocError(new_buf, "realloc buffer")){ free(buffer); // 释放原内存避免泄漏 exit(EXIT_FAILURE); } buffer = new_buf; } buffer[pos] = (char)c; pos++; } // 补充字符串终止符 buffer[pos] = '\0'; // 加换行自动刷新输出缓冲区 printf("exited\n"); return buffer; }
额外说明
- 加break后依然报错的核心原因:无break版本运行时已经发生堆越界写,永真循环会在你无感知的情况下持续写内存,只要多跑几轮就会越过缓冲区边界破坏堆结构,哪怕后续break出循环,调用printf这类隐含堆操作的函数时依然会触发内存校验失败。
- 测试功能时,readLine返回的buffer使用完后记得调用
free()释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者user16817368
相关产品推荐
相关产品推荐

