c/c++使用void*实现类型通配
概述:
我们封装一个函数,比如是一个比较函数,用于比较两个数据的值, 如果我们将参数的类型写死,那么这个函数就只能去处理此种类型的数据,对于其它的类型我们还需要进行函数的重载。
在c++中,模板很好的解决了这个问题,但是C语言中是没有模板的,那我们怎样实现一个函数可以处理多类型的数据呢?
就是使用void*,我们知道任何类型的指针都可以隐式的转换为void*类型的指针,所以,如果我们在参数中以void*类型的量作为参数的话,那么这个函数就可以接收任意类型的地址。进而实现一个函数处理多个类型。
但是,由于void*类型的数据我们不能直接访问或者操作其指向的空间,必须转换成具体的类型才可以。所以,如果我们只是将参数修改成void*类型,自然是不行的。因为你在函数内部,是不知道你要将void*指针转化为什么类型的,自然也没有办法处理其指向的数据。
那么怎么办呢?
我们可以使用函数指针来实现,就是我们在传递void*的时候,还需要根据函数所要实现的功能,提供一个处理void*数据的函数。函数指针指向的函数的参数也应该是void*。
这个函数是我们自己编写,然后传入到调用的函数中去的,所以其内部需要提供一些功能,就是帮助调用函数中需要访问或者操作void*数据的部分,我们可以调用这个函数来实现,因为这个函数使我们自己提供的,我们知道自己此时要处理的具体类型,所以可以根据需求在函数指针指向的函数内部将void*转化为具体的类型,然后进行操作。
下面的代码就使用了void*参数,以及相应的函数指针,来实现通配类型的二分查找。
#include <stdio.h>
#include <stdlib.h>
/*二分查找: 数据必须提前有序,才能使用二分查找,也就是现需要排序*/
/*C语言做到支持多类型,那就使用void*,因为任何类型的指针都可以隐式转换为void*
*/int INT_compare(const void* nub1, const void* nub2) {int* tmp1 = (int*)nub1;int* tmp2 = (int*)nub2;return *tmp1 - *tmp2; // 实现简写
}/*查找到元素返回数组的下标,没有找到返回-1*/
/*elemSize是比较元素类型所占用的字节数,用来获取地址偏移
*/
int BinarySearch(void* arr, int len, int elemSize,void* search,int (*Compare)(const void* , const void*)) {int left = 0, right = 0, middle = 0;/*初始化边界的值,圈定范围的值*/left = 0;right = len - 1;/*没有找到的话,left会大于right*/while (left <= right) {middle = (left + right) / 2; // 还可以使用left + (right - left) / 2 进行计算结果int ret = 0;ret = Compare((char*)arr + middle * elemSize, search); if (!ret) {return middle;}else if(ret > 0){right = middle - 1;}else {left = middle + 1;}}return -1;
}int main(void) {int arr[] = { 1,3,7,9,11 };int search[] = {0,1,7,2,11,12,-1}; // 测试多个数据可以写一个数组,使用for循环测试一堆数据for (int i = 0; i < sizeof(search) / sizeof(search[0]); i++) {int index = BinarySearch(arr, sizeof(arr) / sizeof(arr[0]),sizeof(int) ,&search[i],INT_compare);if (index != -1) printf("%-3d 找到了\n",search[i]);else printf("%-3d 没有找到\n",search[i]);}getchar();return 0;
}
ret = Compare((char*)arr + middle * elemSize, search); // 因为Compare函数指针函数实现了对数据的比较,但是我们在调用函数中传入的是void*类型的数组,每次调用Compare函数传入的是数组对应的某个元素,但是void*的数组我们无法直接访问其内部的元素。(下标访问和指针访问都不可以)。
所以,我们此处将arr转换成(char*),这样你对arr指针加一个数字,就表示其向后移动多少个字节。i表示我们当前遍历数组的第几个元素,arr+i这样指针会向后移动i个字节。
但是,我们传入的数组中的数据并不一定是1个字节的,所以我们需要调用函数的位置,传入一个需要处理的数据占用的字节数elemSize。然后arr+i*elemSize这样arr指针就会指向数组中第i个元素了。