函数指针:首先是一个 指针,指针指向一个函数。
函数指针 写法:函数返回值(*pf)(函数参数列表)
变量类型:函数返回值(*)(函数参数列表)
函数指针数组:首先是一个 数组,数组里面存放的元素是一个个指针,指针指向的对象是 参数相同,返回类型相同 的函数。
函数指针数组 写法:函数返回值(*pfAr[n])(函数参数列表)
变量类型:函数返回值(*)(函数参数列表)[n]
(了解)
指向函数指针数组的指针:首先是一个 指针,指针指向的是一个函数指针数组。
指向函数指针数组的指针 写法:函数返回值(*(*pp)[n])(函数参数列表)
变量类型:函数返回值(*)(函数参数列表)[n]*
/*int Add(int x, int y)
{
return x + y;
}*/
// 1.【函数指针】
int (*pf)(int, int);
// 2.【函数指针数组】
int (*pfAr[4])(int, int);
// pfAr 的类型为去掉变量名 int(*)(int,int)[4]
// 3.【指向'函数指针数组'的指针】
int (*(*pp)[5])(int, int) = &pfArr
//pp 是“指向'函数指针数组'的指针”
//pp 的类型是 int(*)(int,int)[5]*
//pp 指向的数组存放的元素类型是 int(*)(int,int)
函数指针数组,可以用于存放 参数相同、返回类型相同的 一系列函数地址。
即使如此,什么场景下能使用?我们又该如何使用呢?
当我们把返回值和参数相同、功能相似的函数地址放在一个数组里时,这就叫一份 “转移表”。
面对下面实现,原先我们只能使用 switch 调用一个个代码。可以看见的是,switch 逻辑写出的毫无封装可言的代码,甚至经不起位置的调整,面对复杂代码工作量巨大。
如果使用转移表,则避免了程序设计的冗杂,便于后续增添功能和维护。
代码实现一个只有简单加减乘除的计算机:
函数实现:
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("******* 0.exit ********\n");
printf("*************************************\n");
}
主体功能实现:
// 主程序
int main()
{
int input = 0;
int ret = 0;
int x = 0;
int y = 0;
// 转移表
int(* pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };
do {
menu();
printf("请选择:>");
scanf("%d", &input);
if (input >= 1 && input <= 4)
{
printf("请输入两个操作数:>");
scanf("%d %d", &x, &y);
ret = pfArr[input](x, y);//[下标](参数)
printf("%d\n", ret);
}
else if (input == 0)
{
printf("退出计算器");
break;
}
else
{
printf("选择错误");
break;
}
} while (input);
return 0;
}
接着化简:
用一个封装的函数来调用别的函数 -->回调函数
void calc(int (*p)(int, int))
{
int x = 0;
int y = 0;
int ret = 0;
printf("请输入两个操作数:>");
scanf("%d %d", &x, &y);
ret = p(x, y);
printf("%d\n", ret);
}
// 主程序
int main()
{
int input = 0;
int ret = 0;
int x = 0;
int y = 0;
// 转移表
int(* pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };
do {
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
calc(Add);
break;
case 2:
calc(Sub);
break;
case 3:
calc(Mul);
break;
case 4:
calc(Div);
break;
case 0:
printf("退出计算器\n");
break;
defualt:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
回调函数:用一个封装的函数来调用别的函数
面对传统冒泡排序只能对整型排序,我们做如下思考:
2 个整形,用关系运算符比较大小,
2 个字符串,用strcmp比较大小,
2 个结构体,也得指定比较方式…
理想状态:
cmp_int();
cmp_char();
cmp_struct();…
同的类型传入都可以比较,库里面其实是有这个函数的。
qsort
- C语言标准库提供的排序函数 - 可以排序任意类型的数据
void qsort(void *base,
size_t num,
size_t size,
int (*compar)(const void *, const void*));
void base : 待排序数据的起始地址(void 可以接受任何类型的指针)
size_t num :待排序数据的元素个数
size_t size : 待排序数据的元素大小(单位是字节)
int (*compar)(const void , const void) : 比较两个元素大小的函数指针,qsort 接受其返回值 -1/0/1
//解析void*指针类型
int main()
{
char ch = 'w';
int i = 20;
//void*的指针是非常宽容的,可以接收任意类型的地址!!
void* p = &ch;
p = &i;
//void*类型指针,解引用的错误用法
//1.无法直接解引用
//*p = 200;//err
//2.无法++,++的优先级比较高,不能用强制转换
//p++;//err
//(char*)p++;
//正确做法
*(int*)p = 200;//ok,强制类型转换一下就可以了
return 0;
}
想要解决冒泡的单一类型的问题,传参问题解决了
下一个问题,我们知道:
2个整形用关系运算符比较大小
2个字符串,使用strcmp比较大小
2个结构体,也得制定比较方式…
思路:设计一个函数,需要比较什么类型就传进去,以相应的类型进行比较
参照 qsort 的最后一个参数
// int 的比较
/*
if (*(int*)e1 > *(int*)e2)
return 1;
if (*(int*)e1 < *(int*)e2)
return -1;
else
return 0;
*/
int cmp_int(const void* e1, const void* e2)// 要比较的两个元素
{
return (*(int*)e1 - *(int*)e2); // 升序
//return (*(int*)e2 - *(int*)e1); // 降序
}
/*测试 qsort 函数排序 int 数据
void test()
{
int arr[] = { 2,1,3,7,5,6,8,4,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr,sz,sizeof(arr[0]),cmp_int);
print(arr, sz);
}*/
// struct 成员的 比较
/*
struct stu
{
char name[20];
int age;
};
*/
cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp(((struct stu*)e1)->name, ((struct stu*)e1)->name);
}
cmp_stu_by_age(const void* e1, const void* e2)
{
return (((struct stu*)e1)->age - ((struct stu*)e1)->age);
}
/*测试 qsort 函数排序 struct 数据
void test()
{
struct stu s[] = { {"zhangsan",25},{"lisi",21},{"wangwu",30} };
int sz = sizeof(s) / sizeof(s[0]);
//按照名字比较
//qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
//按照年龄比较
qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
*/
//-------------------------qsort()函数用法测试代码结束-----
模仿 qsort 的参数类型,以 bubble 算法,使其能对任意数据类型排序。
// 交换
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
// 排序主体功能
void bubble_sort_all(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{
int i = 0;
int j = 0;
//趟数
for (i = 0; i < sz - 1; i++)//sz-1,是因为拿一个数字比,比较sz-1次
{
//一趟冒泡排序的过程
int aim = 0;
for (j = 0; j < sz - 1 - i; j++)
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
//交换 - 交换 cmp 种比较两个元素地址的每一个字节
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
aim = 1;
}
}
if (aim == 0)
break;
}
}
如果本文对你有些帮助,请给个赞或收藏,你的支持是对作者大大莫大的鼓励!!(✿◡‿◡) 欢迎评论留言~~