移除printf()引发malloc堆损坏:归并排序代码异常求助
问题分析与解决
你的代码存在内存越界破坏堆结构的致命问题,printf("something\n");的存在只是通过改变内存布局暂时掩盖了错误,注释后错误直接暴露。以下是具体原因和修复方案:
核心错误点
1. 归并排序分治拆分完全错误
你当前用(n+1)>>2计算左半数组长度,这是把数组拆成4份,而非归并排序需要的对半拆分。正确的拆分逻辑应该是:
- 左半部分长度:
(n+1)>>1(等价于向上取整n/2) - 右半部分长度:
n - mit(而非n>>2)
错误的拆分导致左右子数组长度之和远小于原数组长度,递归过程和合并逻辑全部混乱,直接引发后续内存访问越界。
2. 合并循环的越界访问
合并阶段的while(i < mit || j < fin)循环存在严重问题:当其中一个子数组遍历完成后(比如i >= mit),代码仍会执行arr[i] <= mit_arr[j]的判断,此时访问的arr[i]已经超出左半数组的合法范围;同理j >= fin时访问mit_arr[j]也会越界。
这种越界访问会写入不属于当前数组的内存区域,破坏glibc堆管理的元数据。堆元数据被破坏后,后续的malloc/free操作就会触发检测机制,抛出malloc(): corrupted top size错误。
为什么注释printf会触发错误?
printf内部会调用malloc分配输出缓冲区,它会改变程序的堆内存布局。当保留printf时,你的越界访问刚好没有破坏堆顶的关键元数据,错误被暂时隐藏;注释掉后,堆布局发生变化,越界访问直接篡改了堆顶块的结构,触发glibc的堆检测终止信号。
修正后的代码片段
#include <stdio.h> #include <stdlib.h> typedef int elem; void mergesort(elem * arr, unsigned n){ if(n <= 1){ return; } // 正确的对半拆分 unsigned mit = (n + 1) >> 1; unsigned fin = n - mit; elem * mit_arr = arr + mit; elem * arr_2 = (elem *)malloc(sizeof(elem) * n); mergesort(arr, mit); mergesort(mit_arr, fin); unsigned i = 0, j = 0, k = 0; // 合并两个有序数组,避免越界 while(i < mit && j < fin){ if(arr[i] <= mit_arr[j]){ arr_2[k++] = arr[i++]; }else{ arr_2[k++] = mit_arr[j++]; } } // 处理左半剩余元素 while(i < mit){ arr_2[k++] = arr[i++]; } // 处理右半剩余元素 while(j < fin){ arr_2[k++] = mit_arr[j++]; } for(unsigned idx=0; idx<n; idx++) arr[idx] = arr_2[idx]; free(arr_2); } int main(){ unsigned a = 10; int i; elem * arr = (elem*)malloc(sizeof(elem) * a); arr[0] = 12; arr[1] = 3; arr[2] = -3; arr[3] = 22; arr[4] = 12; arr[5] = 11; arr[6] = 4; arr[7] = 9; arr[8] = 10; arr[9] = 2; mergesort(arr, a); printf("\n"); for(i=0; i<a; i++){ printf("%d, ", arr[i]); } printf("\n"); free(arr); return 0; }
内容的提问来源于stack exchange,提问作者Mr.Barbo
相关产品推荐
相关产品推荐

