You need to enable JavaScript to run this app. 基于void指针实现任意类型数组通用升序排序,消除重复代码符合DRY要求
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.23 04:57:04