看了一下有些人写的关于函数指针的文章,对于初学者学习起来有点费力,原因很简单——没有具体的代码演示,理解的自然而然不那么深刻强烈,总感觉有点空洞。我今天用大家很清楚的排序代码来说说函数指针好在哪里?
先提一下函数指针与指针函数以免搞混淆,其实很好区分,就按照字面意思来理解,
函数指针:一个指向函数的指针,本质是指针。
void (*fun)(int a,int b);
指针函数:一个返回值为指针的函数,本质是函数。
void *fun(int a,int b);
数组指针和指针数组也是一样的道理,不在赘述。
应用场景:我想要写一个排序的代码,不仅可以实现整数排序、字符排序、小数排序,还可以升序、降序。如果实现这些功能,确实so easy,连续写6个排序的代码。但是这样子造成了大量的冗余代码,如果以后需要继续增加功能的话,又要在里面源代码里面修修改改,违反“开闭原则”,而且代码看起来也不美观。
解决办法:为什么会出现冗余?冗余在哪里了?问题是出在数据类型上,多一个数据类型,就要多写一遍排序的代码。如果我们采用一种办法可以把数据类型和排序代码(算法框架)隔离开,那么再多的数据类型,我们也就写一遍排序代码。 这个时候就需要函数指针登场了。考虑到通用性(屏蔽数据类型)用函数指针隔离变化,用一个接口可以实现升序、降序和不同类型的排序,让用户自己选择怎么去排序。
/*
函数指针简单实现通用通用排序测试用例
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//函数指针隔离变化
typedef int (*Cmp_number)(void *a,void *b);
typedef void (*Print)(void *value);
//关于int
// int类型的数据从小到大
int int_cmp(void *a,void *b)
{
return *(int *)a - *(int *)b;
}
// int类型的数据从大到小
int int_reverse_cmp(void *a,void *b)
{
return *(int *)b - *(int *)a;
}
//打印int类型的数据
void print_int(void *value)
{
printf("%d ",*(int *)value);
}
//关于double
// 关于double类型的数据从小到大
int double_cmp(void *a,void *b)
{
return (int)(*(double *)a - *(double*)b);
}
// 关于double类型的数据从大到小
int double_reverse_cmp(void *a,void *b)
{
return (int)(*(double *)b - *(double*)a);
}
//打印double类型的数据
void print_double(void *value)
{
printf("%.2f ",*(double *)value);
}
//关于char的
int char_cmp(void *a,void *b)
{
return (int)(*(char *)a - *(char *)b);
}
int char_reverse_cmp(void *a,void *b)
{
return (int)(*(char *)b - *(char *)a);
}
void print_char(void *value)
{
printf("%c ",*(char *)value);
}
// 优化选择排序
void select_sort(void **array,int n,Cmp_number cmp)
{
int i = 0;
int j = 0;
int k = 0;
void *data = NULL;
if(array == NULL || n <= 1){
return ;
}
for(i = 0; i < n -1; i++){
k = i;
for(j = i+1; j < n; j++){
if(cmp(array[k],array[j]) > 0){
k = j;
}
}//end for
data = array[i];
array[i] = array[k];
array[k] = data;
}//end for
}
//输出
void print_sort(void **array,int n,Print print_int)
{
int i = 0;
for(i = 0; i < n; i++){
// printf("%d ",*(int**)array[i]);
print_int(array[i]);
}
printf("\n");
}
//主函数
int main(int argc,char *argv[])
{
int i = 0;
void **array = NULL;
int a[10] = {5,2,1,1,2,5,125,521,2,2};
double b[10] = {2.5,5.2,1.1,5.21,2.5,5.21,2.51,1.0,9.1,5.21};
char c[10] = {'r','y','g','d','a','c','h','a','x','g'};
array = malloc(sizeof(void *) *10);
//关于整数的降序
for(i = 0; i < 10; i++){
array[i] = a + i;
}
select_sort(array,10,int_reverse_cmp);
printf("整数降序:");
print_sort(array,10,print_int);
//关于小数的升序
memset(array,0x00,sizeof(array));
for(i = 0; i < 10; i++){
array[i] = b + i;
}
select_sort(array,10,double_cmp);
printf("小数升序: ");
print_sort(array,10,print_double);
//关于字符的升序
memset(array,0x00,sizeof(array));
for(i = 0; i < 10; i++){
array[i] = c + i;
}
select_sort(array,10,char_cmp);
printf("字符升序: ");
print_sort(array,10,print_char);
return 0;
}
上面的排序代码(算法框架)就一个,只是对于不同的类型数据有专门的处理代码,而这些代码可以专门写在一个文件里面,不必在核心代码中出现。
typedef int (*Cmp_number)(void *a,void *b); //比较不同类型的数
typedef void (*Print)(void *value); //打印不同的类型的数
知道了上面这些int ,double, char函数, 通过select_sort(array,array_num,func)接口,只选择合适的func函数调用,用户自己选择。
运行环境
运行结果