回顾一下上节课
函数指针:
void test()
{
printf("haha\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
void(*pf)() = &test;//这个就是函数指针
//void(*pf)() = test;//这样写也可以。函数名就是地址。
test();
(*pf)();//通过pf调用test函数。
pf();//也可以这样写 这个*其实没有意义的。
//函数名就是地址 用地址写也可以。
return 0;
}
函数指针数组;
//实现一个计算器
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");
printf("*********************\n");
printf("请输入序号\n");
}
int main()
{
//在函数指针变量名后面加一个[]就是函数指针数组。
int(*pf[5])(int, int) = {0,Add,Sub,Mul,Div };//这个就是一个函数指针数组。
//返回值 参数类型 0就是空指针,也可以写NULL
int n;
do
{
menu();
//判断输入的合法性;
while (scanf("%d", &n) != EOF)
{
if ((n > 4) && (n<0))
{
printf("输入错误,请重新输入!!\n");
}
if ((n <= 4) && (n >= 0))
{
break;
}
}
if (n == 0)
{
printf("退出计算器!\n");
break;
}
int x, y;
printf("请输入运算的两个值:");
scanf("%d %d", &x, &y);
int red = pf[n](x, y);
printf("运算结果为:%d\n", red);
} while (n);
return 0;
}
7. 指向函数指针数组的指针
就是一个指针 指向函数指针数组的指针
定义:
int main()
{
int arr1[10] = { 1,2,3,4,5,6 };
int(*p1)[10] = &arr1; //p是一个数组指针。
int* arr2[5]; //这个是指针数组
int* (*p2)[5] = &arr2;
//类比
int (*pf)(int, int) = &Add;
//这个是一个函数指针。
int (*pfarr[4])(int, int) = { Add,Sub,Mul,Div };
//函数名和&函数名都是函数的地址 都一样。
//这个是函数指针数组。
//现在我们要写一个 函数指针数组的指针。//每个元素是函数指针。
// 类型为int (*)(int, int)
//指针数组就是 类型 加 (*数组名)[n]
int(*(*p3)[4])(int, int) = &pfarr;
//p3是一个指针,指向函数指针数组的指针。
return 0;
}
使用:
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(*p)(int, int) = Add;
//p是函数指针变量
int (*pfarr[4])(int, int) = { Add, Sub, Mul,Div };
//pfarr是函数指针数组名
int (*(*p3)[4])(int, int) = &pfarr;
//p3指向函数指针数组的指针。
printf("%d",(*p3)[0](3, 4));//7
return 0;
}
8. 回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数 的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进 行响应。
简单说就是:把A函数的指针,作为参数传递给B函数,在B函数内,通过A函数的指针来调用A函数时,我们就说这是一个回调函数。
简单的例子:
void test()
{
printf("haha");
}
void print_haha(void(*p)())
{
if (1)
{
p();
}
}
int main()
{
void (*pf)() = test;
print_haha(pf);
//结果为haha
return 0;
}
用法:(这个可以对第一次写的那个计算器冗余的改进方法。)
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("**********************************\n");
printf("**********1.Add 2.Sub ******\n");
printf("**********3.Mul 4.Div ******\n");
printf("**********0.exit ******\n");
printf("**********************************\n");
printf("**********************************\n");
}
//这个代码就是大大缩减了代码的冗余
void mmm(int (*p)(int, int))//这里用函数指针类型进行接收。
{
printf("请输入两个数字:\n");
int x, y;
scanf("%d%d", &x, &y);
printf("%d\n", p(x, y));
}
//这里用到了回调函数
//在B函数通过地址来调用A函数
int main()
{
int input = 0;
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出计算器!!\n");
break;
case 1:
mmm(Add);
break;
case 2:
mmm(Sub);
break;
case 3:
mmm(Mul);
break;
case 4:
mmm(Div);
break;
default:
printf("输入错误!!!\n");
}
} while (input);
return 0;
}
案例:
qsort函数:
qsort是一个库函数 ,基于快速排序算法实现的一个排序的函数。这个qsort函数对任意类型的数据都可以进行排序。功能很强大。我们写的排序并不是任意数据都可以排序 ,只能排一类数据。
//
// //qsort库函数有四个参数。
// void qsort(void* base, //待排序数据的起始位置,也就数组的首元素地址
//
// size_t num,//数组的元素个数
//
// size_t width, //一个元素的字节大小。
//
// int(* com)(const void* elem1, const void* elem2));
com是比较函数, elem1和elem2是待比较的两个元素的地址。
#include<stdlib.h>
#include<stdio.h>
//这里e1和e2是void*的指针,是一个无确切类型的指针,不能直接进行解引用的
//void* 是用来接收任意类型的指针的。不能直接解引用和加减。
//可以先用()强制类型转换成某一类型的指针就可以使用了。
//而且被const修饰
int cmp(const void* e1, const void* e2)
{
第一种方法。
//if (*(int*)e1 > *(int*)e2)
//{
// return 1;
//}
//else if (*(int*)e1 == *(int*)e2)
//{
// return 0;
//}
//else
//{
// return -1;
//}
//
//第二种方法
return (*(int*)e1 - *(int*)e2);
//如果e1大于e2返回一个正数。
//如果e1大于e2返回一个正数。
//如果e1等于e2返回一个0。
}
int main()
{
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, 10, sizeof(arr[0]), cmp);
//打印结果
for (int i = 0; i < sz; i++)
{
printf("%d", arr[i]);
}
//结果为0123456789
return 0;
}
运用qsort排序字符串。
//使用qsort排序结构体
struct stu
{
char name;//名字
int age;//年龄
float score;//成绩
};
//按照年龄来排的
int cmp_stu_age(const void* e1, const void* e2)//这里的e1和e2是指向结构体的指针。
{
return (((struct stu*)e1)->age - ((struct stu*)e2)->age);
}
//按照名字来排的
int cmp_stu_name(const void* e1, const void* e2)//这里的e1和e2是指向结构体的指针。
{
return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
int main()
{
struct stu arr[3] = {{"liu",21,99.8} , {"guan",19,100.0}, {"zhang", 20,99.9} };
qsort(arr, 3, sizeof(arr[0]), cmp_stu_age);
//这里就实现了按年龄从小到大排序
//可以通过监视窗口自己观看。
return 0;
}
模拟实现qsort函数:
qsort函数有四个参数
1,数组的首地址
2,数组的大小
3,数组每个元素的字节大小
4,判断函数的地址
//模拟qsort函数
struct stu
{
char name[10];
int age;
float scort;
};
//交换任意两个数据的函数实现
//这里不知道是什么类型的数据,但是我们传参的时候把元素所占的字节大小也传了过来
//这样就保证了,我们可以一个字节一个字节的进行交换。
void reverse(char* left, char* right,int width)
{
//单个字符交换。
for (int i = 0; i < width; i++)
{
char mid = *left;
*left = *right;
*right = mid;
right++;
left++;
}
}
//注意这里的void*是为了保证能接收任何类型的函数
my_qsort(void* base, int sz, int width, int (*cmp)(void*, void*))
//注意int(*)(void*)(void*)函数指针类型
{
//接收那个自定义的判断函数。
for (int i = 0; i < sz-1; i++)//这个是排序的趟数
{
//每一趟具体情况
for (int j = 0; j < sz - 1 - i; j++)
{
//cmp函数是自己用qsort函数是写的判断函数要在这里使用
//默认返回大于0的数交换,默认升序
//并不知道base指向的类型,所以不能直接加j,要乘以宽度。
//它的两个参数,是两个元素的首地字节地址,但是void*不能直接加减。
//要先进行强制类型转换。
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
//这个函数是qsort函数的第四个参数指向的函数
{
//任意类型的交换函数,这里要用到每个元素的字节大小。
reverse((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
}
int cmp(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
int main()
{
int arr[4] = { 4,6,2,9 };
int sz = sizeof(arr) / sizeof(arr[0]);
//第一个数组开头的地址,
//第二个数组的元素个数
//每个元素的字节大小
//在自定义的判断函数,返回大于0他就交换。
my_qsort(arr, sz, sizeof(arr[0]), cmp);
//默认升序。
for (int i = 0; i < sz; i++)
{
printf("%d", arr[i]);
}
return 0;
}