如何正确调用归并排序函数对C语言结构体数组排序?
咱们来一步步拆解你遇到的问题,以及如何彻底解决它:
核心问题根源
你原来的merge_sort和merge函数是为指针数组(void**)设计的——它默认数组里存的是指向数据的指针,排序时交换的是指针地址。但你实际要排序的是连续存储的结构体数组(row_t*),数组里直接存的是row_t结构体本身。强行把row_t*转成void**传入,会让函数把结构体的二进制数据当成指针地址来操作,直接触发非法内存访问,导致程序崩溃退出。
另外你最初的比较函数还有个隐藏bug:return ra->S - rb->S用在double类型上会出错——如果两个S的差值是小数(比如0.2),转成int会被截断为0,函数会误判这两个元素相等,这也是你后续遇到段错误的潜在诱因。
适配结构体数组的归并排序实现
要直接排序连续的结构体数组,我们需要修改归并排序函数,让它能根据元素大小来拷贝整个结构体(而不是指针)。修改后的函数需要接收单个元素的大小作为参数:
#include <stdlib.h> #include <string.h> // 归并操作:处理连续内存的数组元素 void merge(void* array, int n, int mid, size_t elem_size, int cmp(const void*, const void*)) { // 分配临时内存存储合并后的结果 void* tmp = malloc(n * elem_size); if (!tmp) return; // 内存分配失败的安全处理 // 用char*来按字节精准访问数组元素 char* arr = (char*)array; char* left = arr; char* right = arr + mid * elem_size; char* tmp_ptr = (char*)tmp; int i = 0, j = 0; const int left_size = mid; const int right_size = n - mid; // 合并两个有序子数组 while (i < left_size && j < right_size) { if (cmp(left + i * elem_size, right + j * elem_size) <= 0) { memcpy(tmp_ptr, left + i * elem_size, elem_size); i++; } else { memcpy(tmp_ptr, right + j * elem_size, elem_size); j++; } tmp_ptr += elem_size; } // 拷贝左半部分剩余元素 memcpy(tmp_ptr, left + i * elem_size, (left_size - i) * elem_size); // 拷贝右半部分剩余元素 tmp_ptr += (left_size - i) * elem_size; memcpy(tmp_ptr, right + j * elem_size, (right_size - j) * elem_size); // 将合并后的结果覆盖回原数组 memcpy(array, tmp, n * elem_size); free(tmp); } void merge_sort(void* array, int n, size_t elem_size, int cmp(const void*, const void*)) { if (n > 1) { int mid = n / 2; // 递归排序左半部分 merge_sort(array, mid, elem_size, cmp); // 递归排序右半部分:通过elem_size计算右半部分的起始地址 merge_sort((char*)array + mid * elem_size, n - mid, elem_size, cmp); // 合并两个有序子数组 merge(array, n, mid, elem_size, cmp); } }
正确的调用方式与比较函数
修正后的比较函数
对于double类型的比较,绝对不能直接用差值转int,应该用逻辑判断返回标准的1/-1/0:
int row_array_s_comp(const void* a, const void* b) { const row_t* ra = (const row_t*)a; const row_t* rb = (const row_t*)b; // 标准浮点比较逻辑:大于返回1,小于返回-1,等于返回0 return (ra->S > rb->S) - (ra->S < rb->S); }
正确的函数调用
传入结构体数组指针、元素总数、单个结构体的大小,以及比较函数:
// 分配100个row_t的内存 row_t* linear_row_arr = (row_t*)malloc(sizeof(row_t)*100); // 先给linear_row_arr的每个元素赋值(比如填充rho、u、v、S等字段)... // 调用归并排序 merge_sort(linear_row_arr, 100, sizeof(row_t), row_array_s_comp);
关于你之前的错误尝试
你之前用merge_sort(linear_row_arr, 100, sizeof(row_t*), row_array_s_comp)导致段错误,是因为传入了sizeof(row_t*)(指针大小,通常8字节)而非sizeof(row_t)(结构体实际大小),这会让函数在拷贝元素时只操作了部分结构体数据,破坏了内存结构,最终引发段错误——和malloc/calloc无关,calloc只是初始化内存为0,不影响这里的逻辑。
内容的提问来源于stack exchange,提问作者eyedea_ability

