目录
1.函数指针
2.函数指针数组(转移表)
指向指针数组的指针
3.回调函数
void*
普信型冒泡排序
1.函数指针
函数指针,顾名思义就是指向函数的指针,存放函数地址。
int test(const char*) {} int (*pf)(const char*)=test;//pf就是test的函数指针。 //那么pf("abc")和(*pf)("abc")和test("abc");其实效果是一样的。
讯享网
下面我们来看两个有趣却又让人头疼的两段代码(代码出处:《c陷阱和缺陷》)
讯享网//代码1 (*(void (*)())0)(); //代码2 void (*signal(int , void(*)(int)))(int);
代码1:首先抓住最关键的0,这个代码主要就是围绕0展开的。前面的括号中(void(*)())其实是一种数据类型,就是我们刚刚讲的函数指针,可能你刚看反应不过来,我们这样写:void(*p)(),这样你是不是就看出他是函数指针了。那么我们在0前面加上函数指针类型,这当然就是将0强制类型转换成无参,返回类型为void的函数地址。那么括号里放的就是一个函数指针,很显然,这个代码就是0函数的依次调用。这个代码其实是程序员想要调用地址为零的程序,其实地址为0一般是开机运行的。
代码2:啊这!!!第一眼看下去肯定会想搁这套娃是吧。我们仔细看,首先signal是啥?如果说是函数指针那么不应该是void(*signal)吗?这显然矛盾了。如果说是函数名,我们拆开来看。函数名前面的*看起来很诡异,其实这是signal函数的返回类型void(* )(int),对的他的返回类型是函数指针,这个最里面的括号实际上是函数的参数。
看到以上这两个代码,特别是第二个代码就感觉很繁琐。我们可以通过类型重命名的方式来简化它。
typedef int(* pft)(int);//将int(*)(int)类型重命名为pft pft signal(int ,pft);//这不是就很好理解了嘛
2.函数指针数组(转移表)
顾名思义:把函数指针放在数组里就是函数指针数组。
那么这个数组有啥用呢?
比如说:计算器的编写
如果不用函数指针数组:
讯享网#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; do { printf( "*\n" ); printf( " 1:add 2:sub \n" ); printf( " 3:mul 4:div \n" ); printf( "*\n" ); printf( "请选择:" ); 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"); breark; default: printf( "选择错误\n" ); break; } } while (input); return 0; }
我们一眼就能看出问题,switch语句里面太冗余了,一样的代码写了几遍,虽然可以复制粘贴再修改,但是不美观。问题出在不同input对应不同情况,每个代码都得再写一遍,实际上涉及到input的函数就一个,但有好多种变化,如何使这几种函数可以通过input变化而相应变化便是关键。我们就可以将几个接口函数指针放入数组,通过改变编号,即可调用所有函数,就可实现简洁了。
#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( "*\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; }
而且我们后期加入更多种接口函数(即功能)时便更轻松。
指向指针数组的指针
如果不停一套娃的话,可以一直套下去,这里就浅套一下
讯享网int(*(ppf)[5]))(int,int);//这里就随便举一例,其他照葫芦画瓢。
3.回调函数
看完定义一头雾水,其实呢函数指针还是很有用的,下面我们举一个常用库函数qsort(快排)的例子。
提到排序很多人想,这不简单嘛,调用int* arr,int size呗,然到只有整形可以排序?浮点数不行?字符串不行?甚至结构体不行?那你说我们经常看到的:按姓氏排序是什么鬼?这么多种类型但程序员不知道我们排哪种呀。那么编写qsort函数的程序员是怎么解决这一问题的呢?

注意int(*compar)(const void*,const void*))这其实就是一个函数指针(回调函数)。
void*
这个compar函数的参数类型是void*。因为不知道待排序的类型是啥,说以使用void*类型。
对于void*类型,我认为他就是一个垃圾桶,啥都可以往里扔,什么类型的指针他都兼容。但鱼和熊掌,不可兼得。我们往里扔爽了,但拿出来可就费功夫了。首先不可以直接解引用,还有啥地址加加减减也不行,->操作还不行。那么怎么办呢?使用前需要强制类型转换。
书接上文啊,关于这个qsort函数的运用,我们需要针对不同的类型,自己分别写多个compar函数才可引用。这里我们给几种事例:
int compar_int(const void* e1, const void* e2) { return (*(int*)e1 - *(int*)e2); }//int形 struct book { char name[20]; int price; }; int compar_book_by_name(const void* e1, const void* e2) { return strcmp(((struct book*)e1)->name, ((struct book*)e2)->name); }//char形,这个呢是结构体关于字符串的排序
然后我们把compar往qsort相应参数位置一填即可。
讯享网void test1() { int a1[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1 }; int n=sizeof(a1) / sizeof(a1[0]); qsort(a1, n, 4, compar_int); for (int i = 0; i < n; i++) { printf("%d ", a1[i]); } printf("\n"); } void test2() { struct book s[3] = { { "bca", 15 }, { "bac", 20 }, { "cab", 5 } }; int n = sizeof(s) / sizeof(s[0]); qsort(s, n,sizeof(s[0]),compar_book_by_name); }
最后,我们来个大手笔
普信型冒泡排序
对于所有种类型都通用的冒泡排序:
void Swap(char* e1, char* e2,int width) { for (int i = 0; i < width; i++) { char tmp = *e1; *e1 = *e2; *e2 = tmp; e1++; e2++; }//因为我们都采用的char*指针,所以对于int形等多字节需要一位一位的交换 } void Bubble_Sort(void* a, int n,int width,int(*compar)(const void* ,const void*)) { int i, j, flag = 0; for (i = 0; i < n - 1; i++) { for (j = 0; j < n - i - 1; j++) {//这里我们采用char*形强制转换,目的是因为char*占一个字节,指针增加减少单位为一个字节 //如果我们用(int*)指针一动就是4个字节,那么char类型将无法排序 if (compar((char*)a+j*width,(char*)a+(j+1)*width)>0)//比较大小用compar函数 { Swap((char*)a + j*width, (char*)a+(j + 1)*width,width);//交换值 flag = 1; } } if (flag == 0) break; } }
如有收获,请给个大大的赞吧。好吧,最后呢祝大家端午节安康。
--2022.6.3

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/53967.html