深入明白指针与回调函数:从底子到实践

[复制链接]
发表于 2025-10-22 01:23:26 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
弁言

在C语言中,指针和回调函数是两个非常紧张的概念。指针为我们提供了直接操纵内存的本领,而回调函数则为我们提供了一种机动的编程方式,使得我们可以将函数作为参数通报给其他函数,从而实现更加模块化和可复用的代码。本文将深入探究指针和回调函数的概念,并通过现实的代码示例来展示它们的应用。
1. 回调函数是什么?


1.1 回调函数的界说


回调函数(Callback Function)是一种通过函数指针调用的函数。简单来说,回调函数是一个在特定变乱或条件发生时被调用的函数。回调函数不是由该函数的实现方直接调用,而是通过函数指针通报给另一个函数,由该函数在得当的机遇调用。

1.2 回调函数的作用

回调函数的紧张作用是解耦代码。通过将函数作为参数通报,我们可以将代码的逻辑分离,使得代码更加模块化和可复用。比方,在一个盘算器步调中,我们可以将加法、减法、乘法、除法等操纵封装成差别的函数,然后通过回调函数的方式将这些函数通报给一个同一的处置处罚函数,从而克制重复的代码。

1.3 回调函数的示例

让我们通过一个简单的例子来明白回调函数的使用。假设我们有一个盘算器步调,用户可以选择差别的操纵(加法、减法、乘法、除法),步调会根据用户的选择实验相应的操纵。

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

在这个代码中,我们使用了switch语句来根据用户的选择调用差别的函数。固然这个代码可以正常工作,但是它存在一个题目:每次添加一个新的操纵时,我们都须要修改switch语句,这会导致代码的冗余和不易维护。

1.3.2 使用回调函数改造后

为了简化代码,我们可以使用回调函数的方式将差别的操纵函数作为参数通报给一个同一的处置处罚函数。如许,当我们添加新的操纵时,只须要添加新的函数,而不须要修改switch语句。
  1. #include <stdio.h>
  2. int add(int a, int b) {
  3.     return a + b;
  4. }
  5. int sub(int a, int b) {
  6.     return a - b;
  7. }
  8. int mul(int a, int b) {
  9.     return a * b;
  10. }
  11. int div(int a, int b) {
  12.     return a / b;
  13. }
  14. void calculate(int (*func)(int, int)) {
  15.     int x, y;
  16.     printf("输入操作数:");
  17.     scanf("%d %d", &x, &y);
  18.     int ret = func(x, y);
  19.     printf("ret = %d\n", ret);
  20. }
  21. int main() {
  22.     int input = 1;
  23.     do {
  24.         printf("**********\n");
  25.         printf(" 1:add \n");
  26.         printf(" 2:sub \n");
  27.         printf(" 3:mul \n");
  28.         printf(" 4:div \n");
  29.         printf(" 0:exit \n");
  30.         printf("**********\n");
  31.         printf("请选择:");
  32.         scanf("%d", &input);
  33.         switch (input) {
  34.             case 1:
  35.                 calculate(add);
  36.                 break;
  37.             case 2:
  38.                 calculate(sub);
  39.                 break;
  40.             case 3:
  41.                 calculate(mul);
  42.                 break;
  43.             case 4:
  44.                 calculate(div);
  45.                 break;
  46.             case 0:
  47.                 printf("退出程序\n");
  48.                 break;
  49.             default:
  50.                 printf("选择错误\n");
  51.                 break;
  52.         }
  53.     } while (input);
  54.     return 0;
  55. }
复制代码
在这个改造后的代码中,我们界说了一个calculate函数,它担当一个函数指针作为参数。这个函数指针指向一个担当两个int参数并返回int的函数。在main函数中,我们根据用户的选择将差别的操纵函数通报给calculate函数,从而实现了代码的简化和模块化。
2. qsort使用举例


2.1 qsort函数简介


qsort是C标准库中的一个函数,用于对数组举行排序。它的原型如下:
  1. void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
复制代码
base:指向要排序的数组的第一个元素的指针。
nmemb:数组中元素的个数。
size:数组中每个元素的巨细(以字节为单元)。
compar:指向比力函数的指针。这个函数用于比力两个元素的巨细。
2.2 使用qsort函数排序整型数据

让我们通过一个简单的例子来演示怎样使用qsort函数对整型数组举行排序。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int int_cmp(const void *p1, const void *p2) {
  4.     return (*(int *)p1 - *(int *)p2);
  5. }
  6. int main() {
  7.     int arr[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0};
  8.     int i = 0;
  9.     qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
  10.     for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
  11.         printf("%d ", arr[i]);
  12.     }
  13.     printf("\n");
  14.     return 0;
  15. }
复制代码
在这个例子中,我们界说了一个int_cmp函数,用于比力两个整型数的巨细。然后我们使用qsort函数对数组arr举行排序。qsort函数会根据int_cmp函数的返回值来决定元素的次序。
2.3 使用qsort排序布局数据

qsort函数不光可以用于排序根本数据范例,还可以用于排序布局体数据。让我们通过一个例子来演示怎样使用qsort函数对布局体数组举行排序。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. struct Stu {
  5.     char name[20];
  6.     int age;
  7. };
  8. int cmp_stu_by_age(const void *e1, const void *e2) {
  9.     return ((struct Stu *)e1)->age - ((struct Stu *)e2)->age;
  10. }
  11. int cmp_stu_by_name(const void *e1, const void *e2) {
  12.     return strcmp(((struct Stu *)e1)->name, ((struct Stu *)e2)->name);
  13. }
  14. void test2() {
  15.     struct Stu s[] = {{"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15}};
  16.     int sz = sizeof(s) / sizeof(s[0]);
  17.     qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
  18.     for (int i = 0; i < sz; i++) {
  19.         printf("%s %d\n", s[i].name, s[i].age);
  20.     }
  21. }
  22. void test3() {
  23.     struct Stu s[] = {{"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15}};
  24.     int sz = sizeof(s) / sizeof(s[0]);
  25.     qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
  26.     for (int i = 0; i < sz; i++) {
  27.         printf("%s %d\n", s[i].name, s[i].age);
  28.     }
  29. }
  30. int main() {
  31.     test2();
  32.     test3();
  33.     return 0;
  34. }
复制代码
在这个例子中,我们界说了一个Stu布局体,包罗门生的姓名和年事。我们界说了两个比力函数cmp_stu_by_age和cmp_stu_by_name,分别用于按照年事和姓名对门生举行排序。然后我们使用qsort函数对布局体数组举行排序,并输出排序后的效果。
3. qsort函数的模仿实现

3.1 冒泡排序简介

冒泡排序是一种简单的排序算法。它重复地遍历要排序的数组,比力相邻的两个元素,假如它们的次序错误就互换它们。遍历数组的工作会重复举行,直到没有须要互换的元素为止。
3.2 使用回调函数模仿实现qsort

我们可以使用冒泡排序的头脑来模仿实现qsort函数。为了支持差别范例的数组,我们须要使用void *指针和回调函数。
  1. #include <stdio.h>
  2. int int_cmp(const void *p1, const void *p2) {
  3.     return (*(int *)p1 - *(int *)p2);
  4. }
  5. void _swap(void *p1, void *p2, int size) {
  6.     int i = 0;
  7.     for (i = 0; i < size; i++) {
  8.         char tmp = *((char *)p1 + i);
  9.         *((char *)p1 + i) = *((char *)p2 + i);
  10.         *((char *)p2 + i) = tmp;
  11.     }
  12. }
  13. void bubble(void *base, int count, int size, int (*cmp)(void *, void *)) {
  14.     int i = 0;
  15.     int j = 0;
  16.     for (i = 0; i < count - 1; i++) {
  17.         for (j = 0; j < count - i - 1; j++) {
  18.             if (cmp((char *)base + j * size, (char *)base + (j + 1) * size) > 0) {
  19.                 _swap((char *)base + j * size, (char *)base + (j + 1) * size, size);
  20.             }
  21.         }
  22.     }
  23. }
  24. int main() {
  25.     int arr[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0};
  26.     int i = 0;
  27.     bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
  28.     for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
  29.         printf("%d ", arr[i]);
  30.     }
  31.     printf("\n");
  32.     return 0;
  33. }
复制代码
在这个代码中,我们界说了一个bubble函数,它担当一个void *指针作为数组的基地点,数组的元素个数,每个元素的巨细,以及一个比力函数。bubble函数使用冒泡排序算法对数组举行排序。为了支持差别范例的数组,我们使用void *指针和_swap函数来互换元素。
4. 总结

通过本文的学习,我们深入明白了指针和回调函数的概念,并通过现实的代码示例展示了它们的应用。回调函数为我们提供了一种机动的编程方式,使得我们可以将函数作为参数通报给其他函数,从而实现更加模块化和可复用的代码。qsort函数是C标准库中的一个强大的排序函数,它通过回调函数的方式支持对差别范例的数据举行排序。我们还通过模仿实现qsort函数,进一步加深了对指针和回调函数的明白。

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表