2025年C语言——指针高阶运用

C语言——指针高阶运用目录 1 函数指针 2 函数指针数组 转移表 指向指针数组的指针 3 回调函数 void 普信型冒泡排序 1 函数指针 函数指针 顾名思义就是指向函数的指针 存放函数地址 int test const char int pf const char

大家好,我是讯享网,很高兴认识大家。

目录

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

小讯
上一篇 2025-02-09 13:19
下一篇 2025-03-09 18:51

相关推荐

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