史上最强C语言教程----指针(进阶部分2)

x33g5p2x  于2022-01-09 转载在 其他  
字(4.4k)|赞(0)|评价(0)|浏览(484)
  1. 函数指针数组

7.回调函数

7.1回调函数的定义

7.2 回调函数的简单应用----简易计算器

7.3 回调函数的应用----qsort()函数

7.4 冒泡排序算法(仿照qsort()的形式)

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

6. 函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组。

比如:

  1. int *arr[10];
  2. //数组的每个元素是int*

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

  1. int (*parr1[10])();
  2. int *parr2[10]();
  3. int (*)() parr3[10];

答案是:parr1 。parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢? 是 int ()() 类型的函数指针。这个地方怎么看出来的呢?我们在数组中知道,**对于数组来说,当我们去掉数组名和[]及[]中的数字之后就知道了数组中元素的类型,**对于上面的parr1,我们也能用类似的方法,当我们将parr1[10]去掉之后,剩下的就是数组元素的类型,即int ()(),这就是数组元素的类型,即函数指针类型!

函数指针数组的用途:转移表

例子:(计算器)

  1. #include <stdio.h>
  2. int add(int a, int b)
  3. {
  4. return a + b;
  5. }
  6. int sub(int a, int b)
  7. {
  8. return a - b;
  9. }
  10. int mul(int a, int b)
  11. {
  12. return a * b;
  13. }
  14. int div(int a, int b)
  15. {
  16. return a / b;
  17. }
  18. int main()
  19. {
  20. int x, y;
  21. int input = 1;
  22. int ret = 0;
  23. int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
  24. while (input)
  25. {
  26. printf("*************************\n");
  27. printf(" 1:add 2:sub \n");
  28. printf(" 3:mul 4:div \n");
  29. printf("*************************\n");
  30. printf("请选择:");
  31. scanf("%d", &input);
  32. if ((input <= 4 && input >= 1))
  33. {
  34. printf("输入操作数:");
  35. scanf("%d %d", &x, &y);
  36. ret = (*p[input])(x, y);
  37. }
  38. else
  39. printf("输入有误\n");
  40. printf("ret = %d\n", ret);
  41. }
  42. return 0;
  43. }

通过上面的程序,我们能够实现简单的计算,并且相比不使用函数指针数组即使用if或者switch进行条件分支的情况,在后续的修改上,我们将会更加的便捷,代码的重复性相对减少!

7.回调函数

7.1回调函数的定义

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

7.2 回调函数的简单应用----简易计算器

实际上,对于我们上面实现的简易计算器,还有另一种实现方法,就是通过回调函数的方法,接下来给大家进行演示一下!

  1. #include <stdio.h>
  2. int add(int a, int b)
  3. {
  4. return a + b;
  5. }
  6. int sub(int a, int b)
  7. {
  8. return a - b;
  9. }
  10. int mul(int a, int b)
  11. {
  12. return a * b;
  13. }
  14. int div(int a, int b)
  15. {
  16. return a / b;
  17. }
  18. void Calc(int (*ptrf)())
  19. {
  20. int x = 0;
  21. int y = 0;
  22. printf("输入操作数:");
  23. scanf("%d %d", &x, &y);
  24. printf("ret = %d\n", ptrf(x,y));
  25. }
  26. int main()
  27. {
  28. int input = 1;
  29. int ret = 0;
  30. do
  31. {
  32. printf("*************************\n");
  33. printf(" 1:add 2:sub \n");
  34. printf(" 3:mul 4:div \n");
  35. printf("*************************\n");
  36. printf("请选择:");
  37. scanf("%d", &input);
  38. switch (input)
  39. {
  40. case 1:
  41. Calc(add);
  42. break;
  43. case 2:
  44. Calc(sub);
  45. break;
  46. case 3:
  47. Calc(mul);
  48. break;
  49. case 4:
  50. Calc(div);
  51. break;
  52. case 0:
  53. printf("退出程序\n");
  54. break;
  55. default:
  56. printf("选择错误,请重新输入!\n");
  57. break;
  58. }
  59. } while (input);
  60. return 0;
  61. }

在上面的这段代码中,在Calc中被调用的函数就是回调函数!

7.3 回调函数的应用----qsort()函数

首先先带大家了解一下qsort()库函数!

在设计比较函数时,要根据下面的进行设计,此时如果按照下面的进行设计的话,就是升序函数!

这个地方呢,相信大家光看我上面列出来的图片大家肯定是看不明白的,下面我将带大家来具体了解一下这个函数的具体使用以及每个参数具体代表的是什么!

  1. //void qsort(void* base, size_t num, size_t width, int(__cdecl* compare)(const void* elem1, const void* elem2));
  2. //base 数组的起始位置
  3. //num 数组元素的个数
  4. //width 每个数组元素所占的字节数
  5. //compare 比较函数
  6. //elm1、elm2:接收要比较的两个元素的地址
  7. //此处为什么是void类型的指针?因为void *可以接收任意类型的指针
  8. //下面是比较整型的运用
  9. #include<stdio.h>
  10. #include<stdlib.h>
  11. int cmp_int(const void* e1, const void* e2)
  12. {
  13. //比较两个整型值的函数
  14. return *(int*)e2 - *(int*)e1;
  15. }
  16. int main()
  17. {
  18. int arr[] = { 1,2,3,4,5 };
  19. int sz = sizeof(arr) / sizeof(arr[0]);
  20. int(*cmp)(const void* e1, const void* e2) = cmp_int;
  21. qsort(arr, sz, sizeof(arr[0]),(*cmp));
  22. for (int i = 0; i < 5; i++)
  23. {
  24. printf("%d ", arr[i]);
  25. }
  26. return 0;
  27. }
  28. //下面是比较结构体类型的运用(通过结构体中student的姓名进行比较)
  29. #include<stdio.h>
  30. #include<stdlib.h>
  31. #include<string.h>
  32. struct student
  33. {
  34. char name[20];
  35. int age;
  36. };
  37. int cmp_by_name(const void* e1, const void* e2)
  38. {
  39. return strcmp(((struct student*)e1)->name, ((struct student*)e2)->name);
  40. }
  41. int main()
  42. {
  43. struct student students[3] = { {"zhangsan",18},{"lisi",19},{"wangwu",20} };
  44. int sz = sizeof(students) / sizeof(students[0]);
  45. qsort(students, sz, sizeof(students[0]), cmp_by_name);
  46. }

上面呢,给大家举了排序整型与结构体元素时的情况,相信大家应该了解了qsort()函数的具体的使用了,下面将是一个比较难的部分,就是我们用冒泡排序的方法自己设计一个能够排序不同元素类型的排序算法。

7.4 冒泡排序算法(仿照qsort()的形式)

  1. void Swap(char* buf1, char* buf2, int width)
  2. {
  3. int i = 0;
  4. for (i = 0; i < width; i++)
  5. {
  6. char tmp = *buf1;
  7. *buf1 = *buf2;
  8. *buf2 = tmp;
  9. buf1++;
  10. buf2++;
  11. }
  12. }
  13. void bubble_sort(void* base, int sz, int width, int(*cmp)(void* e1, void* e2))
  14. {
  15. int i = 0;
  16. for (i = 0; i < sz - 1; i++)
  17. {
  18. int j = 0;
  19. for (j = 0; j < sz - 1 - i; j++)
  20. {
  21. if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
  22. {
  23. Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
  24. }
  25. }
  26. }
  27. }

在这里面起始代码的逻辑还是相对比较复杂的,主要是有些地方不太容易理解,这里我给大家点一个点,就是我们在进行交换的时候,因为我们不知道元素类型是什么,即交换时一次交换几个字节,所以在这个地方我们统一将其转换成char*类型,就是每一次交换的元素数目是一个,交换的次数就是每个元素所占的字节的数目,这个地方并不难理解,但很难想到这种设计的方法,至于其它地方,就与前面的qsort()算法大同小异了,希望大家能够细细体会!

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

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

如何定义?下面就是定义的代码:

  1. #include<stdio.h>
  2. int main()
  3. {
  4. int (*pfarr[4])(int, int);//函数指针数组
  5. int (*(*ppfarr)[4])(int, int) = &pfarr;//函数指针数组指针
  6. //ppfarr是一个数组指针,指针指向的数组有4个元素
  7. //指向的数组的每个元素的类型是一个函数指针 int(*)(int ,int)
  8. return 0;
  9. }

在这个地方,我觉得可能有人问我:这个你是怎么读的啊,这个这么复杂,其实读的方法并不复杂,就是自右向左,由外向内

相关文章