基于void指针实现任意类型数组通用升序排序,消除重复代码符合DRY要求
通用无重复任意类型排序的正确实现
要彻底消除四套重复的排序代码,核心思路是把和具体数据类型绑定的逻辑,和通用排序流程完全解耦——根本不需要把不同类型的指针硬塞给int*这类具体类型的指针变量,只要靠字节偏移定位元素、抽离通用的比较和交换操作,就能一套循环支持所有类型,同时避免类型不匹配的问题。
具体实现步骤
1. 抽离两个不依赖具体类型的基础工具
这两个函数完全不关心传入的是什么数据类型,只做通用内存操作:
- 通用交换函数:接收两个元素的内存地址、单个元素的字节长度,通过内存拷贝完成两块内存的交换
#include <stdlib.h> #include <string.h> static void swap_elem(void* a, void* b, size_t elem_size) { void* temp_buf = malloc(elem_size); memcpy(temp_buf, a, elem_size); memcpy(a, b, elem_size); memcpy(b, temp_buf, elem_size); free(temp_buf); } - 通用比较函数:接收两个元素的内存地址、data_type标识,仅在对应分支内做类型匹配的大小判断,返回标准比较结果(前者大于后者返回1,等于返回0,小于返回-1)
static int compare_elem(const void* a, const void* b, int data_type) { switch(data_type) { case 1: { // int 类型 int val_a = *(const int*)a; int val_b = *(const int*)b; return (val_a > val_b) - (val_a < val_b); } case 2: { // float 类型 float val_a = *(const float*)a; float val_b = *(const float*)b; return (val_a > val_b) - (val_a < val_b); } case 3: { // double 类型 double val_a = *(const double*)a; double val_b = *(const double*)b; return (val_a > val_b) - (val_a < val_b); } case 4: { // char 类型 char val_a = *(const char*)a; char val_b = *(const char*)b; return (val_a > val_b) - (val_a < val_b); } default: return 0; } }
2. 编写唯一一套通用排序主循环
主循环完全不感知数组的具体元素类型,仅通过char*的字节偏移定位元素:
- 先根据data_type建立映射,拿到单个元素占用的字节长度
- 计算第i个元素的地址时,通过
数组基地址 + 下标 * 单元素字节长度的方式定位,不需要把数组强转成int*、float*这类具体类型的指针 - 相邻元素的比较、交换操作全部调用上面抽离的通用函数完成
完整的sort_any_data_type实现:
void sort_any_data_type(void* arr, int size, int data_type) { // 根据data_type映射单元素字节长度 size_t elem_size; switch(data_type) { case 1: elem_size = sizeof(int); break; case 2: elem_size = sizeof(float); break; case 3: elem_size = sizeof(double); break; case 4: elem_size = sizeof(char); break; default: return; // 遇到非法类型直接退出 } // 转成char*做字节级地址计算,完全不需要关心原数组的具体类型 char* base_addr = (char*)arr; // 通用冒泡逻辑,仅编写这一套 for (int i = 0; i < size - 1; i++) { int is_swapped = 0; for (int j = 0; j < size - 1 - i; j++) { // 计算相邻两个元素的实际内存地址 void* curr_elem = base_addr + j * elem_size; void* next_elem = base_addr + (j + 1) * elem_size; // 调用通用比较函数判断顺序 if (compare_elem(curr_elem, next_elem, data_type) > 0) { swap_elem(curr_elem, next_elem, elem_size); is_swapped = 1; } } if (!is_swapped) break; // 优化:无元素交换时提前退出 } }
之前尝试失败的原因
之前想复用一套排序循环时,错误地把float*、double*、char类型的指针直接赋值给int类型的变量,本质是对C语言指针偏移的规则理解有偏差。实际上只要主循环靠字节偏移计算元素地址,根本不需要持有任何具体类型的指针——所有涉及具体类型的强转操作,只在比较函数的分支里短暂完成,不会暴露在通用排序主流程中,自然不会出现类型不匹配的编译错误。
这种实现完全符合DRY设计原则:后续要调整排序算法(比如把冒泡换成快排、插入排序),只需要修改主循环的一处逻辑;要新增支持的数据类型,只需要在elem_size映射、compare_elem分支里补充对应规则,完全不需要改动排序主流程,维护成本会大幅降低。
内容的提问来源于stack exchange,提问作者mRveli
相关产品推荐
相关产品推荐

