C語言實現通用數據結構的高效設計


         近期在閱讀一個開源的C++代碼。里面用到了大量的STL里面的東西。或許是自己一直用C而非常少用C++來實現算法的原因。STL里面大量的模板令人心煩。一直對STL的效率表示懷疑,但在網上搜到這樣一個帖子,說C的標准庫里面高速排序比STL的標准排序要慢!於是,便認真的看了下二者的源代碼,發現C++里面的std::sort綜合運用了部分高速排序和堆排序算法,而C標准庫里面用的是通用數據結構的高速排序,C標准庫里面的qsort之所以比std::sort慢。是由於C語言中為了適配全部的數據結構使用了空指針。以下以更為簡單的插入排序為例說明這個問題。


插入排序的算法實現代碼:

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

        上述插入排序的實現僅僅能針對整數類型進行排序,假設數據類型是浮點型。則要自己又一次把代碼拷貝一份。而且更改函數名以及數據類型。

假設是雙精度的,又或者是對自己定義的結構體數組進行排序呢? 顯然,這不是一種非常好的解決方式。 而用空指針能夠解決問題。

通用數據類型的插入排序實現代碼:

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++模板的代碼也非常easy。


#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[i];\
		j = i;\
		while ((j > low) && (LT_CMP(t, arr[j - 1]))) {\
			arr[j] = arr[j - 1];\
			j--;\
		}\
		arr[j] = t;\
	}\
} 

上面的宏定義能夠看做是一種模板定義,能夠用於隨意數據類型。

假設你要對整數進行排序。非常easy,用以下的兩個宏。一個宏定義比較運算。一個宏為函數定義:

#define  CMP(a, b)    ((a) < (b))
FIV_IMPLEMENT_INSETT_SORT(insert_sort_int, int, CMP)

這樣就有一個用於整數排序的函數insert_sort_int可用,假設是你自己定義的結構體類型,則相同僅僅須要寫這兩個宏就能夠了。


結尾:


用C++模板產生的代碼大小是不使用模板的非常多倍,而用C語言的空指針能夠支持隨意數據類型,代碼大小非常小,而用C語言的宏定義產生模板函數的代碼大小理論上和使用STL的大小是一樣的。

經過本人測試。隨便一個特定數據結構的高速排序遞歸實現,都比c++ stl里面的std::sort要快





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM