C语言使用指针实现泛型
C语言中使用void指针实现更加通用的函数,void指针是一个非常特殊的存在,在ANSI标准中void指针不允许对void指针进行算术运算、解引用操作等。void指针可以指向任意类型的数据,将void指针赋值给其他类型的数据时要使用强制类型转换。
使用void指针实现通用的swap函数:
void swap(void* vp1, void * vp2, int size) {
char* buffer = (char*)malloc(size);
memcpy(buffer, vp1, size);
memcpy(vp1, vp2, size);
memcpy(vp2, buffer, size);
}
通过直接操作内存实现两个变量的交换。
使用void指针实现通用的lsearch(线性查找)函数:
//key为要查找的数,base为数组中的基地址,n为数组元素的个数,elemSize为数组元素的宽度,cmp为数组元素比较的方法(回调函数)
void* lsearch(void* key, void* base, int n, int elemSize, int (*cmp)(void* vp1, void* vp2)) {
for (int i = 0; i < n; ++i) {
void* num = base + elemSize * i;
if (cmp(key, num) == 0) {
return num;
}
}
return NULL;
}
线性查找int类型数据时,调用形式为:
int array[] = {3, 5, 6, 7, 8, 12};
int size = 6;
int target = 7;
lsearch(&target, array, size, sizeof(int), intCmp);
回调函数intCmp()
应该写为:
int intCmp(void* vp1, void* vp2) {
int* num1 = vp1;
int* num2 = vp2;
return *num1 - *num2;
}
线性查找C语言字符串时,调用形式写为
char* array[] = {"hello", "world", "and", "nice", "to", "meet", "you"};
char target[] = "nice";
int size = 7;
lsearch(&target, array, size, sizeof(char*), strCmp);
回调函数strCmp()
应该写为:
int strCmp(void* vp1, void* vp2) {
//调用也可以修改为lsearch(target, array, size, sizeof(char*), strCmp);
//此处str1就只用解一层引用变为:char* str1 = vp1;
char* str1 = *(char**)vp1;
char* str2 = *(char**)vp2;//因为这里调用时传入为指针的指针因此要解两层引用
return strcmp(str1, str2);
}
回调函数应该为普通的全局函数或者类中的静态函数,不应该为类中非静态函数。
使用void指针实现通用的rotate函数(数据交换)
函数原型为void rotate(void* front, void* middle, void* end)
,函数是将front指针与middle指针之间的数值与middle指针和end指针之间的数值进行交换,如下图所示。
达到如下的效果如下图
具体实现为
void rotate(void* front, void* middle, void* end) {
int frontSize = (char*)middle - (char*)front;
int backSize = (char*)end - (char*)middle;
char buffer[frontSize];
memcpy(buffer, front, frontSize);
memmove(front, middle, backSize);
memcpy((char*)front + backSize, buffer, frontSize);
}
此处使用了memcpy和memmove函数,两个函数都是将一块内存中的内容复制到另一块内存中。区别是如果移动过程中有内存重叠的部分,memcpy可能会将之后使用到的内存内容进行覆盖,而memmove函数则会采用合适的方式进行内存复制以避免出现问题。因此memmove函数相比较于memcpy在执行效率上较低,因此一般除非特殊情况,否则采用memcpy。