目录
函数指针数组
在指针(3)中已经提到了函数指针的概念,接下来我们直接学习函数指针数组。
函数指针数组其实就是:声明一个数组,里面存储的类型是,指向函数的指针。
int add(int x, int y)//函数声明
{
return x + y;
}int sub(int x, int y)
{
return x - y;
}int mul(int x, int y)
{
return x * y;
}int div(int x, int y)
{
return x / y;
}int main()
{//函数指针
/*int(*pf)(int, int) = add;*///函数指针数组
int(*pf[4])(int, int) = {add, sub, mul,div};//存放函数指针的数组--函数指针数组for (int i = 0; i < 4; i++)//通过数组元素调用函数
{
int t=pf[i](1,1);
printf("%d\n", t);
}
}
回调函数
什么是回调函数?
回调函数就是一个通过函数指针调用的函数;
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
例如,我们写一个代码实现计算器的功能:
大部分都会写出和下面类似的代码:
#include<stdio.h>
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
return x / y;
}
void menu()
{
printf("***********************************\n");
printf("**********1.add******2.sub*********\n");
printf("**********3.mul******4.div*********\n");
printf("***********************************\n");
}
int main()
{
int input;
int x, y;
int t;
do
{
menu();
printf("请选择操作:\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入要操作的数:\n");
scanf("%d%d", &x, &y);
t = add(x, y);
printf("%d\n", t);
break;
case 2:
printf("请输入要操作的数:\n");
scanf("%d%d", &x, &y);
t = sub(x, y);
printf("%d\n", t);
break;
case 3:
printf("请输入要操作的数:\n");
scanf("%d%d", &x, &y);
t = mul(x, y);
printf("%d\n", t);
break;
case 4:
printf("请输入要操作的数:\n");
scanf("%d%d", &x, &y);
t = div(x, y);
printf("%d\n", t);
break;
case 0:
printf("退出!\n");
break;
default:
printf("输入不合理!请重新输入\n");
break;
}
} while (input != 0);
}
我们发现在主函数里面出现了相似的代码:
printf("请输入要操作的数:\n");
scanf("%d%d", &x, &y);
t = add(x, y);
printf("%d\n", t);
这样看会显得我们写的代码过于冗余,那怎么简化一下呢?
我们发现在相似的代码里面,只有函数调用时出现了不同,我们可以把函数调用的地址以参数的形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数;
#include<stdio.h>
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
return x / y;
}
void menu()
{
printf("***********************************\n");
printf("**********1.add******2.sub*********\n");
printf("**********3.mul******4.div*********\n");
printf("***********************************\n");
}
void fun(int (*pf)(int,int))
{
int x, y;
int t;
printf("请输入要操作的数:\n");
scanf("%d%d", &x, &y);
t =pf(x, y);
printf("%d\n", t);
}
int main()
{
int input;
do
{
menu();
printf("请选择操作:\n");
scanf("%d", &input);
switch (input)
{
case 1:
fun(add);
break;
case 2:
fun(sub);
break;
case 3:
fun(mul);
break;
case 4:
fun(div);
break;
case 0:
printf("退出!\n");
break;
default:
printf("输入不合理!请重新输入\n");
break;
}
} while (input!=0);
}
回调函数:
void fun(int (*pf)(int,int))
{
int x, y;
int t;
printf("请输入要操作的数:\n");
scanf("%d%d", &x, &y);
t =pf(x, y);
printf("%d\n", t);
}
qsort
qsort是一个库函数,可以直接使用,用来任意类型数据的排序;
声明: void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));void qsort(
void* base,//base指向了排序数组中的第一个元素
size_t num,//base指向的数组中的元素个数
size_t size,//base指向的数组中的元素大小(单位是字节)
int(*compar)(const void* p1, const void* p2)//比较方式
);
qosrt的使用举例
排序整型数据:
int com(const void* p1, const void* p2)
{
return (*(int*)p1) - (*(int*)p2);
//如果p1>p2,那么p1-p2的值大于0,返回值大于0;
//如果p1<p2,那么返回值小于0;
}void Print(int arr[],int sz)//打印数组
{
for (int i = 0; i < sz; i++)
printf("%d ", arr[i]);
}
void test1()
{
int arr[10] = { 1,3,2,4,6,5,0,9,8,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), com);//实现的是升序
Print(arr, sz);
}
排序结构体数据:
struct stu
{
char name[20];
int age;
};//按年龄来比较
int com_age(const void* p1, const void* p2)
{
return ((struct stu*)p1)->age - ((struct stu*)p2)->age;
}//按名字来比较
int com_name(const void* p1, const void* p2)
{
return strcmp((struct stu*)p1, (struct stu*)p2);
}
void Print1(struct stu arr[], int sz)//打印
{
for (int i = 0; i < sz; i++)
printf("%s %d ", arr[i].name,arr[i].age);
printf("\n");
}
void test2()
{
struct stu st[] = { {"Li",23},{"Wang",24},{"Yang",20} };
int sz = sizeof(st) / sizeof(st[0]);
qsort(st, sz, sizeof(st[0]), com_age);
Print1(st, sz);
qsort(st, sz, sizeof(st[0]), com_name);
Print1(st, sz);
}
qsort的模拟实现
我们先写一个冒泡排序,在冒泡排序的前提下模拟实现qsort:
void bubble_sort(int arr[], int sz)//参数要重新设计
{
for (int i = 0; i < sz; i++)
{
for (int j = 0; j <sz-1-i ; j++)
{
if (arr[j] > arr[j + 1])//比较的地方要使用回调函数改造
{
int t = arr[j];
arr[j] = arr[j + 1];//交换代码也需要改造
arr[j + 1] = t;
}
}
}
}
由于我们使用的冒泡排序传递的参数是数组,不能给除数组外的其他类型数据(如结构体等)排序,这里我们使用void* base,以指针作为参数进行传递;
我们传递的参数是指针类型,我们就需要考虑如何用指针来表示arr[j] ,这里我们就需要引入一个新的参数:width,即:(char *)base+j*width;
这样我们就可以表示if (arr[j] > arr[j + 1])为cmp((char *)base + j * width, (char*)base + (j + 1) * width)>0,这里传递cmp函数即可;
int(*cmp)(const void *p1,const void *p2)
如何交换呢:可以一个字节一个字节的交换,这样如果是结构体数据类型,也可以通用;
最后就是如何设计来实现交换:
void swap(char *b1,char *b2,int width)
{
for (int i = 0; i < width; i++)
{
char tmp = *b1;
*b1 = *b2;
*b2 = tmp;
b1++;
b2++;
}}
修改后 :
int com(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}void swap(char *b1,char *b2,int width)
{
for (int i = 0; i < width; i++)
{
char tmp = *b1;
*b1 = *b2;
*b2 = tmp;
b1++;
b2++;
}}
void bubble_sort2(void *base,size_t sz,size_t width,int(*cmp)(const void *p1,const void *p2))
{
for (int i = 0; i < sz; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
//if (arr[j] > arr[j + 1])
if (cmp((char *)base + j * width, (char*)base + (j + 1) * width)>0)
{
/*int t = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = t;*/
swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
}void test3()
{
int arr[] = { 3,1,2,4,5,7,6,0,9,8 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort2(arr, sz,sizeof(arr[0]),com);Print(arr, sz);
}