函数指针

指针一直都是c语言中的难点,但是指针在使用中,更多的情况是指向数组、变量等类型。而函数指针一般比较少直接运用。对于函数指针,最常见的两个用途就是转移表和作为参数传递给另一个函数。函数指针也和其他指针一样,对函数指针进行访问前必须把它初始化为指向某个函数。
首先讲一下回调函数,回调函数是指用户把一个函数指针作为参数传递给其他函数,后者将“回调”用户所传递的函数,使用这样技巧的函数称回调函数。举个例子来说,在某个时刻,需要比较两个整形值的大小,从而进行相应的操作,这样的情况下,当然可以通过编写一个参数为整形的比较函数来实现,在需要调用的地方进行调用,但是如果在代码中另一处又想比较浮点型的参数的大小呢,是不是又要编写一个浮点型参数的比较函数,假如还要比较字符串呢(当然了字符串的话,有库函数可以调用,这里只是举例说明情况而已),可以看到不同类型的比较,都需要相应的接口函数实现,那么能不能实现一个函数,只管传递参数,不用管参数的类型,也能得到正确的比较结果,答案是可以的。这样一来就可以统一对外提供的接口,做到便于代码的管理,所使用的技巧就是回调函数。下面就以实现比较函数的回调函数来作为例子,来说明如何实现回调函数的用法。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int compare_type_int(void const *va, void const *vb)
{
    int a = *(int *)va;
    int b = *(int *)vb;
    if (a == b)
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

int compare_type_str(void const *va, void const *vb)
{
    char *a = (char *)va;
    char *b = (char *)vb;
    return strcmp(a,b);
}

int compare_function(void const *ta, void const *tb, int (*compare)(void const *a, void const *b))
{
    return compare(ta, tb);
}

int main(void)
{
    int val_a = 5;
    int val_b = 5;
    char *str_a = "abc";
    char *str_b = "abd";

    if (compare_function((void const *)&val_a, (void const *)&val_b, compare_type_int) == 0)
        printf("val_a == val_b\n");
    else 
        printf("val_a != val_b\n");

    if (compare_function((void const *)&str_a, (void const *)&str_b, compare_type_str) == 0)
        printf("str_a == str_b\n");
    else
        printf("str_a != str_b\n");
    system("pause");
    return 0;
}

函数指针的第二用法就是实现转移表。以整形数的加减乘除操作为例来说明转移表的实现方式(这里旨在说明用法,不作函数健壮性判断)。

#include <stdio.h>
#include <stdlib.h>

int ope_add(int a, int b)
{
    return a + b;
}

int ope_sub(int a, int b)
{
    return a - b;
}

int ope_mul(int a, int b)
{
    return a * b;
}

int ope_div(int a, int b)
{
    return a / b;   
}

int main(void)
{
    int a;
    int b;
    int oper_type;
    int result;

    scanf("%d%d%d",&a, &b, &oper_type);
    switch(oper_type)
    {
    case 1:
            result = ope_add(a,b);
            break;
    case 2:
            result = ope_sub(a, b);
            break;
    case 3:
            result = ope_mul(a, b);
            break;
    case 4:
            result = ope_div(a, b);
            break;
    }
    printf("result = %d\n",result);
    system("pause");
    return 0;
}

可以看到,对于指定的操作类型,在实现中调用相应的函数。但是如果这样的操作类型远不止例子中的4个,则switch中的case语句将会是相当多的,整个函数的代码会也因此而变得很长,不利于程序阅读和维护。这时就可以用转移表来作替代实现。代码如下:

#include <stdio.h>
#include <stdlib.h>

int ope_add(int a, int b)
{
    return a + b;
}

int ope_sub(int a, int b)
{
    return a - b;
}

int ope_mul(int a, int b)
{
    return a * b;
}

int ope_div(int a, int b)
{
    return a / b;   
}

int (*oper_fun[])(int a, int b) = {ope_add, ope_sub, ope_mul, ope_div};
int main(void)
{
    int a;
    int b;
    int oper_type;
    int result;

    scanf("%d%d%d",&a, &b, &oper_type);

    if (oper_type>= 0 && oper_type <= 3)
        result = oper_fun[oper_type](a, b);
    else
    {
        printf("no such operation\n");
        return -1;
    }
    printf("result = %d\n",result);
    system("pause");
    return 0;
}

定义一个函数指针数组,这样当需要添加新的操作类型的函数时,只需要定义该类型的函数,并添加数组成员,就可以了。对于操作类型oper_type要做一个安全性判断,如果发生下标越界访问,在一个复杂的程序代码中,将会是很难调节的,因为其结果是未定义的,并不能知道程序什么时候会运行失败,所以在写代码时,做好这一步健壮性判断是必须的。但是假如某程序中使用了转移表,而在实现调试过程中,怀疑转移表访问出问题了,可以在那个函数调用前后各打印一条信息。因为如果转移表访问越界了,函数调用是不会返回的。用这种办法就可以判断是不是转移表访问越界了。
转移表虽然方便,但也是也有限制,就是转移表内的函数类型要相同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值