目录
函数指针
看一段代码
再举个栗子
函数指针数组
举个栗子
函数指针数组的用途:转移表
函数指针数组指针
回调函数
回调函数的使用
qsort函数
使用回调函数,模拟实现qsort
数组指针是指向数组的指针;
那么函数指针不难理解,是指向函数的指针,存放函数地址的指针。
#include
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
输出的结果
输出的是两个地址,这两个地址是 test 函数的地址。 那我们的函数的地址怎么保存呢?
void test()
{
printf("hehe\n");
}
void (*pfun1)();
void *pfun2();
pfun1和pfun2哪个有能力存放test函数的地址?
pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。
#include
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
printf("%d\n", Add(a, b));
//&函数名 和 函数名 都是函数的地址
printf("%p\n", Add);
printf("%p\n", &Add);
int(*pa)(int, int) = Add;//函数指针的存储 int(*)(int,int) - 变量类型
printf("%d\n",(*pa)(2, 3));//5 - 推荐写法 - 好理解
printf("%d\n",(pa)(2, 3));//5
printf("%d\n",(**pa)(2, 3));//5
printf("%d\n",(***pa)(2, 3));//5
//这里的*其实只是摆设,没有作用
return 0;
}
数组是一个存放相同类型数据的存储空间,前面已经学习了指针数组,比如:
int *arr[10];
把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
答案是:parr1
parr1 先和 [ ] 结合,说明parr1是数组,数组的内容是 int (*)() 类型的函数指针。
#include
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(*parr[4])(int, int) = { Add,Sub,Mul,Div };
int i = 0;
for (i = 0; i < 4; i++)
{
printf("%d\n", parr[i](2, 3));
}
return 0;
}
例子:实现计算器
#include
void menu()
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf(" 0:exit \n");
printf("*************************\n");
}
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 input = 0;
// int x = 0;
// int y = 0;
// do
// {
// menu();
// printf("请选择:");
// scanf("%d", &input);
//
// switch (input)
// {
// case 1:
// printf("请输入两个整数:");
// scanf("%d%d", &x, &y);
// printf("%d\n", Add(x, y));
// break;
// case 2:
// printf("请输入两个整数:");
// scanf("%d%d", &x, &y);
// printf("%d\n", Sub(x, y));
// break;
// case 3:
// printf("请输入两个整数:");
// scanf("%d%d", &x, &y);
// printf("%d\n", Mul(x, y));
// break;
// case 4:
// printf("请输入两个整数:");
// scanf("%d%d", &x, &y);
// printf("%d\n", Div(x, y));
// break;
// case 0:
// printf("退出\n");
// break;
// default:
// printf("选择错误\n");
// break;
// }
// } while (input);
//
// return 0;
//}
//利用函数指针数组写法
int main()
{
int input = 0;
int x = 0;
int y = 0;
int(*pfArr[])(int, int) = { 0,Add,Sub,Mul,Div };//pfArr是一个函数指针数组 - 转移表
do
{
menu();
printf("请选择:");
scanf("%d", &input);
if(input>=1&&input<=4)
{
printf("请输入两个操作数:");
scanf("%d%d", &x, &y);
int ret = pfArr[input](x,y);
printf("%d\n", ret);
}
else if (input == 0)
{
printf("退出\n");
}
else
{
printf("选择错误\n");
}
} while (input);
return 0;
}
函数指针数组指针,即指向函数指针数组的指针,是一个指针,指针指向一个 数组 ,数组的元素都是函数指针。
那么它如何定义呢?看代码
#include
int main()
{
int arr[10] = { 0 };
int(*p)[10] = &arr; //取出数组的地址
int(*pf)(int, int);//pf是一个函数指针
int(*pfArr[4])(int, int);//pfArr是一个函数指针数组
int(*(*ppfArr)[4])(int, int) = &pfArr;//ppfArr是一个函数指针数组指针
return 0;
}
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该 函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
举例演示
#include
void print(char arr[])
{
printf("%s\n",arr);
}
void test(void (*p)(char))
{
p("haha");
}
int main()
{
test(print);
return 0;
}
输出结果
首先演示一下qsort函数的使用
//qsort - quick sort - 可以排序任意类型的数据
//void qsort(void* base,size_t num, size_t width, int(__cdecl* compare)(const void* elem1, const void* elem2));
//4个参数:
//1.目标数组的首元素地址
//2.数组大小(以元素为单位)
//3.元素大小(字节)
//4.比较函数
//
//void* 类型可以接受任意类型的指针
//因为无类型无法确定访问的空间大小 - 不能进行解引用(*)操作 - 不能进行+-整数的操作
//
//排序整型数组
#include
#include//qsort
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;//规定e1,返回大于0的值
}
int main()
{
int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr,sz , sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
采用冒泡的方式
#include
#include
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
int cmp_float(const void* e1, const void* e2)
{
if (*(float*)e1 < *(float*)e2)
return -1;
else if (*(float*)e1 > *(float*)e2)
return 1;
else
return 0;
}
struct Stu
{
char name[20];
int age;
};
int cmp_stu_by_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
//return (*(struct Stu*)e1).age - (*(struct Stu*)e2).age;
}
int cmp_stu_by_name(const void* e1, const void* e2)
{
//比较名字是比较字符串
//字符串不能直接用>、<比较,应用strcmp函数
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *(buf1 + i);
*(buf1 + i) = *(buf2 + i);
*(buf2 + i) = tmp;
}
}
//实现bubble_sort函数的程序员不知道未来排序的数据类型 - void*
//那便也不知道待比较的两个元素类型 - void*
void bubble_sort(void* base, int sz, int width,int(*cmp)(void* e1,void* e2))
{
int i = 0;
//趟数
for (i = 0; i < sz - 1; i++)
{
int j = 0;
//每趟比较的对数
for (j = 0; j < sz - 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);
}
}
}
}
void test1()
{
int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
//使用bubble_sort函数的程序员知道自己要排序的数据
//也就知道如何比较排序数组中的元素 - cmp_int
bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void test2()
{
float f[] = { 9.0,8.0,7.0,6.0,5.0,4.0 };
int sz = sizeof(f) / sizeof(f[0]);
bubble_sort(f, sz, sizeof(f[0]), cmp_float);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%.1f ", f[i]);//%.nf - 保留n为小数
}
printf("\n");
}
void test3()
{
struct Stu s[3] = { {"张三",20},{"李四",30},{"王五",10} };
int sz = sizeof(s) / sizeof(s[0]);
//以年龄排序
//qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
//以名字比较
bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s %d\n", s[i].name, s[i].age);
}
return 0;
}
int main()
{
test1();//int
test2();//float
test3();//结构体数组
return 0;
}
学完指针,在练习题专栏有对应的指针笔试题哦,快去练习巩固一下吧~