C语言深入理解指针(五):回调函数与函数指针数组的奇妙旅程

宝子们,我们在指针的江湖里摸爬滚打了不少时日啦,从最开始的懵懂,到现在对字符指针、数组指针、函数指针等的熟练运用,是不是感觉自己的 C 语言武功已经小有成啦?不过,咱不能停歇,今天咱得继续深挖,把回调函数和函数指针数组这两个高阶技能拿下!它们在实际项目里超实用的,能让你的代码灵活度和重用性瞬间拉满。别慌,咱一步一步来,保证新手也能轻松上手!

 

回调函数:函数指针的奇妙应用

 

回调函数,听着是不是有点高深?其实呀,它就是通过函数指针被调用的函数。啥意思呢?就是说,当一个函数的指针作为参数,被传递给另一个函数时,这个被调用的函数就叫回调函数。它不是由自己的实现方直接调用,而是在特定事件发生时,由另一方来调用,主要用来响应事件或条件。是不是有点像“你先去做别的,等我需要的时候再叫你过来帮忙”的意思?

 

咱举个栗子🌰!假设我们有个函数,专门用来做不同的数学运算,比如加法、减法、乘法。传统写法是不是得把所有逻辑都写在一个函数里,一堆 if-else 判断来决定做什么运算?要是用回调函数,咱就可以把每种运算写成单独的函数,然后把这些函数的指针当作参数传递给主函数。主函数需要做啥运算,直接调用对应的函数指针就行啦。这样不仅代码简洁,而且扩展性超好,以后想加新的运算,直接写个新函数,传给主函数就行,不用动原来主函数的代码!

 

#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; }

 

// 主函数,第三个参数是函数指针(回调函数)

void calculate(int a, int b, int (*func)(int, int)) {

    printf("Result: %d\n", func(a, b)); // 调用回调函数

}

 

int main() {

    // 使用不同的回调函数,实现不同的运算

    calculate(5, 3, add); // 输出 8

    calculate(5, 3, sub); // 输出 2

    calculate(5, 3, mul); // 输出 15

    return 0;

}

 

看到没?这就是回调函数的魅力!它让函数调用变得灵活又可控,把主函数和具体实现函数解耦,主函数不用管具体是加减乘除,只要按照约定好的函数签名(参数类型和返回值类型)调用就行。

 

 

函数指针数组:批量管理回调函数的神器

 

要是你的程序需要支持多种运算,或者有好多不同的回调函数,一个个传是不是太麻烦啦?这时候,函数指针数组就闪亮登场咯!它就像一个“函数仓库”,把所有相关的函数指针都存起来,用的时候直接取出来就行。

 

还是拿数学运算来举例,咱们定义一个函数指针数组,把 add、sub、mul 这些函数的指针都存进去。然后根据用户输入或者程序逻辑,从数组里取出对应的函数指针,调用执行就行。这样,不管有多少种运算,只要往数组里一丢,管理起来轻松又方便。

 

#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 main() {

    // 定义函数指针数组,存放各种运算函数的指针

    int (*func_array[3])(int, int) = {add, sub, mul};

    int a = 5, b = 3;

 

    // 根据索引调用不同的回调函数

    printf("%d + %d = %d\n", a, b, func_array[0](a, b)); // 输出 5 + 3 = 8

    printf("%d - %d = %d\n", a, b, func_array[1](a, b)); // 输出 5 - 3 = 2

    printf("%d * %d = %d\n", a, b, func_array[2](a, b)); // 输出 5 * 3 = 15

 

    return 0;

}

 

是不是很方便呀?函数指针数组就像是一个“函数集合”,把所有的可能性都准备好,到时候按需取用就行。特别适合那些需要动态选择不同功能的场景,比如图形界面的事件处理、不同算法的切换等。

 

 

qsort 的使用:函数指针数组和回调函数的经典案例

 

C 标准库里的`qsort`函数就是一个绝佳的例子,把函数指针数组和回调函数用得明明白白。`qsort`是一个通用的排序函数,能给各种类型的数据排序。它怎么做到对不同数据类型都适用的呢?就是靠回调函数!它通过一个函数指针参数,把比较函数的逻辑交给使用者来定义。使用者只需要按照约定的函数签名(返回值类型是 int,两个参数是 const void)写好比较函数,然后传给`qsort`就行。`qsort`内部调用这个比较函数,决定元素的大小关系,从而实现排序。

 

比如,给一个整数数组排序:

 

#include <stdio.h>

#include <stdlib.h>

 

// 比较函数,用于升序排序

int compare_ints(const void *a, const void *b) {

    int arg1 = *(const int*)a;

    int arg2 = *(const int*)b;

    return arg1 - arg2;

}

 

int main() {

    int arr[] = {5, 3, 7, 1, 9, 2};

    int n = sizeof(arr) / sizeof(arr[0]);

 

    // 使用 qsort 对数组排序,compare_ints 就是回调函数

    qsort(arr, n, sizeof(int), compare_ints);

 

    printf("Sorted array: ");

    for (int i = 0; i < n; i++) {

        printf("%d ", arr[i]);

    }

    return 0;

}

 

这就是`qsort`的厉害之处,它通过回调函数,把排序的具体逻辑和数据的比较逻辑分离,让使用者能根据自己的需求定制比较规则。不管是整数、浮点数还是字符串,只要写好对应的比较函数就行。

 

 

qsort 函数的模拟实现:深入理解背后的原理

 

了解了`qsort`的使用后,咱不妨来简单模拟一下它的实现,这样能更透彻地理解它背后的原理。其实`qsort`通常采用快速排序算法,它通过分区、递归的方式对数组进行排序。在模拟实现时,我们得定义好函数的参数,除了数组本身,还有数组元素个数、元素大小(用 sizeof 得到),以及比较函数指针。

 

#include <stdio.h>

#include <stdlib.h>

 

// 模拟 qsort 的回调函数,升序排序

int compare_ints(const void *a, const void *b) {

    return (*(int*)a - *(int*)b);

}

 

// 快速排序算法实现(简化版)

void quicksort(void *arr, int n, size_t size, int (*compare)(const void*, const void*)) {

    if (n <= 1) return;

 

    char *array = (char*)arr;

    int pivot_index = n / 2;

    char pivot_value[size];

    memcpy(pivot_value, array + pivot_index * size, size);

 

    int left = 0, right = n - 1;

    while (left <= right) {

        // 找到左边比基准大的元素

        while (compare(array + left * size, pivot_value) < 0) left++;

        // 找到右边比基准小的元素

        while (compare(array + right * size, pivot_value) > 0) right--;

        if (left <= right) {

            // 交换元素

            char temp[size];

            memcpy(temp, array + left * size, size);

            memcpy(array + left * size, array + right * size, size);

            memcpy(array + right * size, temp, size);

            left++;

            right--;

        }

    }

 

    // 递归排序子数组

    quicksort(arr, left, size, compare);

    quicksort(array + (left) * size, n - left, size, compare);

}

 

int main() {

    int arr[] = {5, 3, 7, 1, 9, 2};

    int n = sizeof(arr) / sizeof(arr[0]);

 

    // 使用自定义的 quicksort 函数排序

    quicksort(arr, n, sizeof(int), compare_ints);

 

    printf("Sorted array: ");

    for (int i = 0; i < n; i++) {

        printf("%d ", arr[i]);

    }

    return 0;

}

 

虽然这个模拟实现没有完全重现标准库`qsort`的所有优化细节,但它展示了`qsort`内部的基本原理。通过回调函数来比较元素,快速排序算法的分区和递归逻辑来实现排序。

 

总结

 

宝子们,今天咱们把回调函数、函数指针数组以及`qsort`函数的使用和模拟实现都拿下啦!回调函数让函数调用变得更加灵活,函数指针数组则是批量管理回调函数的利器,而`qsort`则是它们的经典应用案例。通过这些知识,我们不仅能写出更灵活、可扩展的代码,还能深入理解 C 标准库函数的设计思想。是不是感觉自己的 C 语言武功又精进了?这下不管是面对复杂的运算逻辑,还是各种排序场景,都能轻松应对啦!

 

宝子们,码到这里是不是对回调函数和函数指针数组的威力有了更深的感触呀?有没有在实际项目里用过这些技巧呢?或者有没有想到新的应用场景?快到评论区和大家分享分享你的想法和经验吧!说不定你的灵感就能点亮别人呢!下次,咱继续在指针的江湖里闯荡,探索更多未知的宝藏,敬请期待哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值