6. 函数指针数组
6.1 什么是函数指针数组
类比指针数组
存放字符指针:char* arr1[10];
存放整型指针:int* arr2[5];
函数指针数组:存放函数指针的数组
写法:
#include<stdio.h>
int main()
{
//函数指针数组
int(*pf[5])(int, int); //pf和[]先结合,说明pf是数组,数组的内容是 int(*)(int int)类型的函数指针
//指向函数指针数组的指针
int(*(*ppf)[5])(int, int) = &pf;
return 0;
}
6.2 函数指针数组的用途——转移表
6.2.1 实现一个简易版计算器(switch语句)
主要功能:实现两个数字的加减乘除
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
void manu()
{
printf("====================\n");
printf("====1:add 2:sub===\n");
printf("====3.mul 4:div===\n");
printf("====0:exit ====\n");
printf("===================\n");
}
int main()
{
int input = 0;
int x, y;
int ret = 0;
do
{
manu();
printf("请输入您的选择:\n");
scanf("%d", &input);
switch(input)
{
case 1:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("您已退出计算器\n");
break;
default:
printf("您的输入有误,请重新输入:\n");
}
} while (input);
return 0;
}

分析,我们发现每个case语言都是类似的,如果功能更多,switch语句只会越来越多,实在是太繁琐了
这时候就用到我们的函数指针数组了
6.2.2 使用函数指针数组实现一个简易版的计算器——转移表
分析:
通过使用一个函数指针数组,大大简化了我们的代码,以后需要添加其他功能
只需要修改函数指针数组内容,还有具体功能实现,再就是在while中判断条件进行一个小修改
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
while (input)
{
printf("====================\n");
printf("====1:add 2:sub===\n");
printf("====3.mul 4:div===\n");
printf("====0:exit ====\n");
printf("===================\n");
printf("请选择:");
scanf("%d", &input);
if ((input <= 4 && input >= 1))
{
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = (*p[input])(x, y);
}
else
printf("输入有误\n");
printf("ret = %d\n", ret);
}
return 0;
}

7. 指向函数指针数组的指针
7.1 什么是指向函数指针数组的指针
从函数指针数组去改写指向函数指针数组的指针,不容易出错。
#include<stdio.h>
int main()
{
//函数指针数组
int(*pf[5])(int, int); //pf和[]先结合,说明pf是数组,数组的内容是 int(*)(int int)类型的函数指针
//指向函数指针数组的指针
int(*(*ppf)[5])(int, int) = &pf; //ppf先和*结合,说明是指针,把(*ppf)取掉,就是我们指针指向的函数指针数组 int(*)[5](int,int)
return 0;
}
分析
指向函数指针数组的指针的一个很重要的用途就是回调函数,下面介绍什么是回调函数
8. 回调函数
8.1 什么是回调函数
定义:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这个被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
8.2 使用函数指针调用 回调函数 实现一个简易版的计算器
在6.2.1代码的基础上进行改进,构建一个函数,函数参数就是函数指针
分析
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
void manu()
{
printf("====================\n");
printf("====1:add 2:sub===\n");
printf("====3.mul 4:div===\n");
printf("====0:exit ====\n");
printf("===================\n");
}
void clc(int(*pf)(int,int))
{
int x = 0;
int y = 0;
int ret = 0;
printf("请输入两个操作数:>");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("%d\n", ret);
}
int main()
{
int input = 0;
int x, y;
int ret = 0;
do
{
manu();
printf("请输入您的选择:\n");
scanf("%d", &input);
switch(input)
{
case 1:
clc(add);
break;
case 2:
clc(sub);
break;
case 3:
clc(mul);
break;
case 4:
clc(div);
break;
case 0:
printf("您已退出计算器\n");
break;
default:
printf("您的输入有误,请重新输入:\n");
break;
}
} while (input);
return 0;
}

8.3 结合8.2的实例我们再理解下我们的回调函数

8.4 回调函数的使用——库函数qsort
在我们的cplusplus中搜下我们的qsort
该函数一共有四个参数:
base:数组首元素的地址
num:数组的长度
size:在数组中每个元素的大小,int类型的大小就是siziof(int);
compar:一个比较函数
对于compar函数的返回值,这里也做了规定,
8.4.1 使用库函数qsort实现整形的排序
我们要调用qsort函数对数组进行排序,需要实现一个比较函数,这个函数要根据你要比较的对象类型来实现比较函数。
#include<stdio.h>
#include<stdlib.h>
int int_com(const void* p1, const void* p2) //这里的参数设置为void*是因为你不知道传过来的数据究竟是什么类型的,
//加const是因为我们只是对数据进行比较,不希望这里对数据数值进行一个修改,所以加了const
{
return (*(int*)p1) - (*(int*)p2);
}
//我们要调用qsort函数对数组进行排序,需要实现一个比较函数,这个函数要根据你要比较的对象类型来实现比较函数。
int main()
{ //使用qsort对数组进行排序,升序
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_com);
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}

8.4.2 使用库函数qsort实现结构体的排序
#include<stdio.h>
#include<stdlib.h>
struct student
{
char name[20];//名字
int age;//年龄
};
int com_byAge(const void* e1, const void* e2)
{
return ((struct student*)e1)->age - ((struct student*)e2)->age;
}
//我们要调用qsort函数对数组进行排序,需要实现一个比较函数,这个函数要根据你要比较的对象类型来实现比较函数。
int main()
{ //使用qsort对结构体进行排序
struct student s[] = { {"zhangsan",17},{"lisi",27} ,{"wangwu",21} };
qsort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), com_byAge);
return 0;
}
- 先根据年龄排序
调试查看我们的结构体,排序前顺序
调试后
- 根据名字排序
根据名字排序,所以比较两个字符串,这里调用到了我们的字符串比较函数strcmp()
#include<stdio.h>
#include<stdlib.h>
struct student
{
char name[20];//名字
int age;//年龄
};
int com_byAge(const void* e1, const void* e2)
{
return ((struct student*)e1)->age - ((struct student*)e2)->age;
}
int com_byName(const void* e1, const void* e2)
{
return strcmp(((struct student*)e1)->name , ((struct student*)e2)->name);
}
//我们要调用qsort函数对数组进行排序,需要实现一个比较函数,这个函数要根据你要比较的对象类型来实现比较函数。
int main()
{ //使用qsort对结构体进行排序
struct student s[] = { {"zhangsan",17},{"lisi",27} ,{"wangwu",21} };
//qsort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), com_byAge);
qsort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), com_byName);
return 0;
}
调试查看调用函数之前前
调用函数之后
8.4.3 使用冒泡排序实现qsort函数的功能
1. 先实现我们的冒泡排序
思路:
代码实现
#include<stdio.h>
void bubbySort(int arr[],int size)
{
int i = 0;
for (i = 0; i < size - 1; i++)//本层循环控制比较躺数
{
int j = 0;
for (j = 0; j < size - 1 - i; j++)//本层循环控制每趟比较次数
{
if (arr[j] < arr[j + 1])
{
int tem = arr[j];
arr[j] = arr[j+ 1];
arr[j + 1] = tem;
}
}
}
}
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9};
int size = sizeof(arr) / sizeof(arr[0]);
bubbySort(arr, size);
int i = 0;
for (i = 0; i < size ; i++)
{
printf("%d ", arr[i]);
}
return 0;
}

目前的冒泡排序只能实现int类型的排序,那我们想实现其他类型如何修改函数
2. 模仿qsort的功能实现一个通用的冒泡排序
修改冒泡排序函数,使其能够排序任意类型数据
仿照我们的sort函数去改造我们的冒泡排序
思路:传参我们和sort一样
关键点1:在每趟比较的时候,我们需要表示清楚我们进行比较的前一个元素和后一个元素
我们不清楚传进来的是什么类型的数据,可能的int可能是结构体,所以我们按字节大小来进行前后移动,来表示前一个和后一个,所以我们将传入的元素强制类型转换为(char*)类型
前一个元素:(char* )base + j * width, j初值是0 ,所以这里加j*width就是加0,表示首元素的地址
后一个元素:(char *)base + (j + 1) * width) ,这里就是我们的首元素地址+一个元素大小,这也是为什么我们第三个参数要传我们数据的大小的原因
关键点2:当我们判断,前一个元素小于后一个元素的时候就要进行交换
这里交换,我们不能直接写死,不能写int或者其他,但是不管它是什么类型,我们可以让它内部一个字节一个字节的交换,直到交换传入这个数据的一个大小(Width)
- 比较整形
//一个通用的冒泡排序
#include<stdio.h>
void swap(char* m, char* n, int width) //我们拿到相邻的两个元素,和大小,然后全部一个一个的交换
{
int i = 0;
for (i = 0; i < width; i++)
{
char tem = *m;
*m = *n;
*n = tem;
m++;
n++;
}
}
void bubbySort(void* base,size_t size,size_t width,int(*cmp)(const void* e1,const void* e2))
{
size_t i = 0;
for (i = 0; i < size - 1; i++)//本层循环控制比较趟数
{
size_t j = 0;
for (j = 0; j < size - 1 - i; j++)//本层循环控制每趟比较次数
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
//交换
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
cmp_int(const void* e1, const void* e2)
{
return *(int*)e1- *(int*)e2;
}
void test1()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int size = sizeof(arr) / sizeof(arr[0]);
bubbySort(arr, size, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < size; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
test1();//比较整形
return 0;
}

2. 比较结构体
//一个通用的冒泡排序
#include<stdio.h>
void swap(char* m, char* n, int width) //我们拿到相邻的两个元素,和大小,然后全部一个一个的交换
{
int i = 0;
for (i = 0; i < width; i++)
{
char tem = *m;
*m = *n;
*n = tem;
m++;
n++;
}
}
void bubbySort(void* base,size_t size,size_t width,int(*cmp)(const void* e1,const void* e2))
{
size_t i = 0;
for (i = 0; i < size - 1; i++)//本层循环控制比较趟数
{
size_t j = 0;
for (j = 0; j < size - 1 - i; j++)//本层循环控制每趟比较次数
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
//交换
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
cmp_int(const void* e1, const void* e2)
{
return *(int*)e1- *(int*)e2;
}
struct student
{
char name[20];//名字
int age;//年龄
};
int com_byAge(const void* e1, const void* e2)
{
return ((struct student*)e1)->age - ((struct student*)e2)->age;
}
int com_byName(const void* e1, const void* e2)
{
return strcmp(((struct student*)e1)->name , ((struct student*)e2)->name);
}
void test1()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int size = sizeof(arr) / sizeof(arr[0]);
bubbySort(arr, size, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < size; i++)
{
printf("%d ", arr[i]);
}
}
void test2()
{
struct student s[] = { {"zhangsan",17},{"lisi",27} ,{"wangwu",21} };
bubbySort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), com_byAge);
bubbySort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), com_byName);
}
int main()
{
//test1();//比较整形
test2();//比较结构体
return 0;
}












1282

被折叠的 条评论
为什么被折叠?



