利用C实现泛型(generics)效果---原地洗牌,快速排序

用一个函数实现一个算法,能够应用在不同的数据类型(int,double,字符数组)上,这在C++,python等高级语言中是很常见的。

灵活地利用C中的void指针,内存操作,函数指针也可以实现类似的效果。

用void指针指向特定元素的地址,如果传入元素占内存大小(如sizeof(int),sizeof(double)),可以用memcpy实现赋值操作。

如果实现了用于元素间比较或四则运算的函数并将其指针传入(如int (*cmp)(void*,void*)),则能在算法中不考虑数据类型实现更多功能。

比如下面一段函数,实现了原地洗牌,快速排序两种算法:

#include <stdlib.h>
#include <string.h>
#include <time.h>

//omit ptr check when malloc and realloc 

void swap(void* ap, void* bp, int elem_size) {
	void* buffer = malloc(elem_size);
	memcpy(buffer, ap, elem_size);
	memcpy(ap, bp, elem_size);
	memcpy(bp, buffer, elem_size);
	free(buffer);
}

void randomize_in_place(void* arr, int n, int elem_size) {
	int rand_place;
	srand((unsigned int)time(NULL));
	for(int i=0; i<n; i++) {
		rand_place = rand() % (n-i) + i;
		swap((char*)arr+i*elem_size, (char*)arr+rand_place*elem_size, elem_size);
	}
}

int int_cmp(const void* ap, const void* bp) {
	return *(int*)ap - *(int*)bp;
}

int double_cmp(const void* ap, const void* bp) {
	if(*(double*)ap - *(double*)bp < 0)
		return -1;
	else if(*(double*)ap - *(double*)bp == 0)
		return 0;
	else
		return 1;
}

int charptr_cmp(const void* ap, const void* bp) {
	return strcmp(*(char**)ap, *(char**)bp);
}

int randomized_partition(void* arr, int p, int r, int elem_size, int (*compar)(const void*, const void*)) {
	srand((unsigned int)time(NULL));
	int rand_elem_index = rand() % (r-p+1) + p;
	char* rand_elem_ptr = (char*)arr + rand_elem_index * elem_size;
	char* pivot_ptr = (char*)arr + r * elem_size;
	swap(rand_elem_ptr, pivot_ptr, elem_size);
	// start to partition, arr[r]=pivot
	// p<=k<=i arr[k]<=pivot, i+1<=k<=j-1 arr[k]>pivot 
	int i = p - 1;
	char* ith_elem_ptr = NULL;
	char* jth_elem_ptr = NULL;
	for(int j=p; j<r; j++) {
		jth_elem_ptr = (char*)arr + j * elem_size;
		if(compar(jth_elem_ptr, pivot_ptr) <= 0) {
			i = i + 1;
			ith_elem_ptr = (char*)arr + i * elem_size;
			swap(ith_elem_ptr, jth_elem_ptr, elem_size);
		}
	}
	swap((char*)arr+(i+1)*elem_size, pivot_ptr, elem_size);
	return i+1;
}

void recursive_qsort(void* arr, int p, int r, int elem_size, int (*compar)(const void*, const void*)) {
	if(p < r) {
		int q = randomized_partition(arr, p, r, elem_size, compar);
		recursive_qsort(arr, p, q-1, elem_size, compar);
		recursive_qsort(arr, q+1, r, elem_size, compar);
	}
}

void generic_qsort(void* arr, int n, int elem_size, int (*compar)(const void*, const void*)) {
	recursive_qsort(arr, 0, n-1, elem_size, compar);
}

int main(int argc, char** argv) {
	int int_array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
	randomize_in_place(int_array, 10, sizeof(int));  //test
	generic_qsort(int_array, 10, sizeof(int), int_cmp);  //test
	double double_array[6] = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
	randomize_in_place(double_array, 6, sizeof(double));  //test
	generic_qsort(double_array, 6, sizeof(double), double_cmp);  //test
	char* charptr_array[6] = {"generics", "hccc", "spring", "summer", "autumn", "winter"};
	randomize_in_place(charptr_array, 6, sizeof(char*));  //test
	generic_qsort(charptr_array, 6, sizeof(char*), charptr_cmp);  //test
}

在《C++编程思想》那本书中的,“一个袖珍的C库”那一小节,利用类似的思想实现了一个stash,不过那里使用的是字符拷贝,而且只考虑了元素存取,没有更复杂的操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值