指针的进阶

  • 5.函数指针
  • 6.函数指针数组
  • 7.指向函数指针数组的指针
  • 8.回调函数

5.函数指针

数组指针 – 指向数组的指针 – 存放的是数组的地址 – &数组名就是数组的地址。
函数指针 – 指向函数的指针 – 存放的是函数的地址 – &函数名就是函数的地址。

#define _CRT_SECURE_NO_WARNINGS 1#include int Add(int x, int y){return x + y;}int main(){//&函数名就是函数的地址//函数名也是函数的地址printf("%p\n", &Add);printf("%p\n", Add);int (*pd1)(int, int) = &Add;//int (*pd2)(int, int) = Add;//pd2是函数指针变量int ret = (*pd1)(3, 5);printf("%d\n", ret);return 0;}


阅读下面这两段代码:

//代码1:(*(void (*)())0)();//void (*)()为函数指针类型//下面这段代码是在调用0地址处的函数,这个函数没有参数,返回类型是void//代码2:void (*signal(int , void(*)(int)))(int);//signal函数的参数有两个,第一个是int类型,第二个是函数指针类型,该类型是void(*)(int),该函数指向的类型是int,返回类型是void.signal函数也是函数指针类型,该类型是void(*)(int),该函数指向的类型是int,返回类型是void.

6.函数指针数组

指针数组:
字符指针数组 – 数组 – 存放的是字符指针
整型指针数组 – 数组 – 存放的是整型指针
那么函数指针数组 – 数组 – 存放的是函数指针 – 存放的是函数的地址。

#include int Add(int x, int y){return x + y;}int Sub(int x, int y){return x - y;}int main(){int (*pf1)(int, int) = &Add;int (*pf2)(int, int) = ⋐int (*pfArr[4])(int, int) = { &Add,&Sub };//pfArr是函数指针数组,是存放函数指针的数组 return 0;}

这里我们来模拟一下计算器的实现:

#define _CRT_SECURE_NO_WARNINGS 1#include void menu(){printf("******************************\n");printf("*** 1.add2.sub ***\n");printf("*** 3.mul4.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;int ret = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Add(x, y);printf("ret = %d\n", ret);break;case 2:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Div(x, y);printf("ret = %d\n", ret);break;case 0:printf("退出程序!\n");break;default:printf("选择错误!\n");break;}} while (input);return 0;}

✅自测运行:

程序不足:如果想要实现更多的功能||、&&、&、|、>>、<<等等,还得重复写一些步骤,就很冗余!
这里,我们就可以利用函数指针数组的知识来优化它,函数指针数组的作用:转移表!

#define _CRT_SECURE_NO_WARNINGS 1#include void menu(){printf("******************************\n");printf("*** 1.add2.sub ***\n");printf("*** 3.mul4.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;int ret = 0;do{menu();printf("请选择:>");scanf("%d", &input);//函数指针数组-转移表int (*pfarr[])(int, int) = { NULL,Add,Sub,Mul,Div };if (0 == input){printf("退出程序!\n");}else if (input >= 1 && input <= 4){printf("请输入2个操作数:");scanf("%d%d", &x, &y);ret = pfarr[input](x, y);printf("ret = %d\n", ret);}else{printf("选择错误,请重新选择!\n");}} while (input);return 0;}

7.指向函数指针数组的指针

指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针。

如何定义?

void test(const char* str){printf("%s\n", str);}int main(){//函数指针pfunvoid (*pfun)(const char*) = test;//函数指针的数组pfunArrvoid (*pfunArr[5])(const char* str);pfunArr[0] = test;//指向函数指针数组pfunArr的指针ppfunArrvoid (*(*ppfunArr)[5])(const char*) = &pfunArr;return 0;}

8.回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

这里,我们利用回调函数实现一下计算器:

#define _CRT_SECURE_NO_WARNINGS 1#include void menu(){printf("******************************\n");printf("*** 1.add2.sub ***\n");printf("*** 3.mul4.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;}void calc(int (*pf)(int, int)){int x = 0;int y = 0;int ret = 0;printf("请输入2个操作数:");scanf("%d%d", &x, &y);ret = pf(x, y);printf("ret = %d\n", ret);}int main(){int input = 0;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;default:printf("选择错误!\n");break;}} while (input);return 0;}

✅自测运行:

这里,我们演示一个qsort函数的实现:

qsort函数是一个库函数,底层使用的是快速排序的方式,对数据进行排序的这个函数可以直接使用,这个函数可以用来排序任意类型的数据。

我们先一起回顾一下冒泡排序的内容:

//冒泡排序算法://给一组整型数据,然后使用冒泡排序算法,对数据进行升序排列//冒泡排序的思想:两两相邻的元素进行比较#include void bubble_sort(int arr[], int sz){int i = 0;for (i = 0; i < sz - 1; i++){int j = 0;for (j = 0; j < sz - 1 - i; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}}void print_arr(int *arr,int sz){int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");}int main(){int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);//排序前bubble_sort(arr, sz);print_arr(arr, sz);//排序后return 0;}

缺陷:我们发现这个函数不够通用,只能排序整型类型!
关于qsort函数,我们可以先去cpluplus网站上面了解一下:

//1.排序整型数组,两个整型可以直接使用大于号比较。//2.排序结构体数组,两个结构体数据可能不能直接使用大于号比较。//不同类型的数据,比较出大小,方法是有差异的。void qsort (void* base//待排序数组的第一个元素的地址, size_t num//待排序数组的元素个数, size_t size//待排序数组中一个元素的大小, int (*compar)(const void* e1,const void* e2));//函数指针compar指向了一个函数,这个函数是用来比较两个元素的//e1和e2中存放的是需要比较的两个元素的地址

关于void*类型的指针:

  1. void*类型的指针 – 不能进行解引用操作符,也不能进行±整数的操作
  2. void*类型的指针是用来存放任意类型数据的地址
  3. void*无具体类型的指针
#include int int_cmp(const void* p1, const void* p2){return (*(int*)p1 - *(int*)p2);}void _swap(void* p1, void* p2, int size){int i = 0;for (i = 0; i < size; i++){char tmp = *((char*)p1 + i);*((char*)p1 + i) = *((char*)p2 + i);*((char*)p2 + i) = tmp;}}void bubble(void* base, int count, int size, int(*cmp)(void*, void*)){int i = 0;int j = 0;for (i = 0; i < count - 1; i++){for (j = 0; j < count - i - 1; j++){if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0){_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}}int main(){int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };//char *arr[] = {"aaaa","dddd","cccc","bbbb"};int i = 0;bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}printf("\n");return 0;}

好啦,关于进阶指针(中)的知识点到这里就结束啦,后期会继续更新C语言的相关知识,欢迎大家持续关注、点赞和评论!❤️❤️❤️