[讨论] (转载)C语言实现通用数据结构的高效设计

247153481   2015-6-1 09:37 楼主

使用宏替代模板的方案

原地址 http://blog.csdn.net/celerychen2009/article/details/40655239

最近在阅读一个开源的C++代码,里面用到了大量的STL里面的东西。也许是自己一直用C而很少用C++来实现算法的原因,STL里面大量的模板令人心烦。一直对STL的效率表示怀疑,但在网上搜到这样一个帖子,说C的标准库里面快速排序比STL的标准排序要慢!于是,便认真的看了下二者的源码,发现C++里面的std::sort综合运用了部分快速排序和堆排序算法,而C标准库里面用的是通用数据结构的快速排序,C标准库里面的qsort之所以比std::sort慢,是因为C语言中为了适配所有的数据结构使用了空指针。下面以更为简单的插入排序为例说明这个问题。


插入排序的算法实现代码:


[cpp] view plaincopy


  • void insert_sort(int a[], int n)   
  • {   
  •     int i, j;   
  •     int t;  
  •     for (i = 1; i < n; i++) {  
  •         t = a;  
  •         j = i;  
  •         while ((j > 0) && (a[j - 1] > t)) {  
  •             a[j] = a[j - 1];  
  •             j--;  
  •         }  
  •         a[j] = t;  
  •     }  
  • }   


        上述插入排序的实现只能针对整数类型进行排序,如果数据类型是浮点型,则要自己重新把代码拷贝一份,并且更改函数名以及数据类型。如果是双精度的,又或者是对自定义的结构体数组进行排序呢? 显然,这不是一种很好的解决方案。 而用空指针可以解决这个问题。

通用数据类型的插入排序实现代码:


[cpp] view plaincopy


  • void general_insert_sort(void* arr, int num_element, int element_bytes, int (*cmp_fun)(void* p1, void* p2))   
  • {   
  •     int i, j;   
  •     int t[1024];  
  •     if (element_bytes > 4096){  
  •         return;  
  •     }  
  • #define ELE(arr, i) (void*)(((unsigned)arr) + (i) * element_bytes)  
  •     for (i = 1; i < num_element; i++) {  
  •         memcpy(t, ELE(arr, i), element_bytes);  
  •         j = i;  
  •         while ((j > 0) && (cmp_fun(ELE(arr, j - 1), (void*)t))) {  
  •             memcpy(ELE(arr, j), ELE(arr, j - 1), element_bytes);  
  •             j--;  
  •         }  
  •         memcpy(ELE(arr, j), (void*)t, element_bytes);  
  •     }  
  • }   


        上述通用插入排序的实现有一个限制,就是待排序数组里面每一个元素的大小不能超过4k,当然对于简单的系统预定义好的数据类型,数组元素的大小最大为double,只有8个字节,这是远远的足够用的。如果你自定义的结构体的大小太大,例如大于这里设置的4K,则没有必要用此方法排序,因为此时数据移动会占用大部分时间,此时应该考虑用索引排序的方法。

       上面的方法虽然解决了任意数据类型的问题,但是其效率并不怎么高。相对于上述第一段代码而言,简单的赋值语句必须得调用一个函数来拷贝数据,简单的比较语句,则需要调用外部传入一个函数指针得到比较结果。这是效率低下的根本原因。

       而C++模板参数的出现,只需要写一份代码,编译器根据你调用时候的数据类型自动生成新的代码。其实用宏也可以完成通用的功能。这里给出C语言宏的代码,C++模板的代码也很简单。



[cpp] view plaincopy


  • #define FIV_IMPLEMENT_INSETT_SORT(function_name, T, LT_CMP)\  
  • void function_name(T* arr, int low, int high)\  
  • {\  
  •     int i, j;\  
  •     T t;\  
  •     for (i = low + 1; i < high; i++) {\  
  •         t = arr;\  
  •         j = i;\  
  •         while ((j > low) && (LT_CMP(t, arr[j - 1]))) {\  
  •             arr[j] = arr[j - 1];\  
  •             j--;\  
  •         }\  
  •         arr[j] = t;\  
  •     }\  
  • }   


上面的宏定义可以看做是一种模板定义,可以用于任意数据类型。如果你要对整数进行排序,很简单,用下面的两个宏,一个宏定义比较运算,一个宏为函数定义:

[cpp] view plaincopy


  • #define  CMP(a, b)    ((a) < (b))  

[cpp] view plaincopy


  • FIV_IMPLEMENT_INSETT_SORT(insert_sort_int, int, CMP)  


这样就有一个用于整数排序的函数insert_sort_int可用,如果是你自定义的结构体类型,则同样只需要写这两个宏就可以了。


结尾:


用C++模板产生的代码大小是不使用模板的很多倍,而用C语言的空指针可以支持任意数据类型,代码大小很小,而用C语言的宏定义产生模板函数的代码大小理论上和使用STL的大小是一样的。经过本人测试,随便一个特定数据结构的快速排序递归实现,都比c++ stl里面的std::sort要快




回复评论 (6)

前面关于“发现C++里面的std::sort综合运用了部分快速排序和堆排序算法,而C标准库里面用的是通用数据结构的快速排序,”这部分不置评。其余大部分属于瞎说:不要用C的眼光来看C++(和模板)。
默认摸鱼,再摸鱼。2022、9、28
点赞  2015-6-1 12:42
引用: freebsder 发表于 2015-6-1 12:42
前面关于“发现C++里面的std::sort综合运用了部分快速排序和堆排序算法,而C标准库里面用的是通用数据结构的快速排序,”这部分不置评。其余大部分属于瞎说:不要用C的眼光来看C++(和模板)。

多谢指出,我是注意到他说的使用宏作为c语言中替代模板的一种方案还挺好
点赞  2015-6-1 13:04
引用: 247153481 发表于 2015-6-1 13:04 多谢指出,我是注意到他说的使用宏作为c语言中替代模板的一种方案还挺好
两个概念的东西。宏只是替换,而模板是一个完整的语言,这是天壤之别。 说个最简单的例子:模板在一份代码里由编译器自动实例化,比如你要一份int的,一份float的。而宏,你需要float和int的那就自己实例化两次,编译的时候,编译器不会告诉你重复定义?这时候你在函数后缀下面加上一个_int或者_float来区分吧,两个函数了,但是 < 符号怎么办?下次>符号,下次 <= 符号?你再实例化2^n次方的实例出来。下次不比较float了,比较struct1在下次struct2,再弄个_struct1来区分函数,再弄个struct2来区分,再下次需要比较struct1和struct2,把它们排序嘛,再弄个_strcut1_struct2,当然你不能指定第一个参数和第二个参数谁前谁后,他们本来是同质的,那再弄个_struct2_struct1。。。几何级数增加的一个函数,还叫通用吗?那还要这种宏替换做什么? 可不能抓住(void*)就叫通用啦。呵呵 本帖最后由 freebsder 于 2015-6-1 13:34 编辑
默认摸鱼,再摸鱼。2022、9、28
点赞  2015-6-1 13:30
引用: freebsder 发表于 2015-6-1 13:30
两个概念的东西。宏只是替换,而模板是一个完整的语言,这是天壤之别。
说个最简单的例子:模板在一份代码里由编译器自动实例化,比如你要一份int的,一份float的。而宏,你需要float和int的那就自己实例化两次,编译的时候,编译器不会告诉你重复定义?这时候你在函数后缀下面加上一个_int或者_float来区分吧,两个函数了,但是 < 符号怎么办?下次>符号,下次

层主大神,学习了,小弟想请教一下,那如果想在c语言中实现模板,改如何呢?或者说有没有可能实现
点赞  2015-6-1 15:53
引用: 247153481 发表于 2015-6-1 15:53
层主大神,学习了,小弟想请教一下,那如果想在c语言中实现模板,改如何呢?或者说有没有可能实现

C语言现有的标准和语法实现不了,放弃吧。
默认摸鱼,再摸鱼。2022、9、28
点赞  2015-6-1 16:24
都是大神啊,小弟学习了
点赞  2015-6-8 23:19
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复