函数指针与函数指针数组

关于函数的地址

#include <stdio.h>
void test()
{}

int main()
{
    printf("%p\n" ,test);		//当前编译器给出的地址0x400526
    printf("%p\n" ,&test);		//当前编译器给出的地址0x400526
    return 0;
}

总结:输出的是两个地址,这两个地址是test函数的地址就一个,不存在什么函数首地址这种说法。那么我们可以把地址交给一个函数指针变量存起来。

1、函数指针

什么是函数指针?

  •   函数指针:本质上是一个指针,这个指针指向了一个函数的入口地址。
    
  • 如:int (*p)(int ,int); //函数指针的定义
  • int (*p)(); //函数指针的另一种定义方式,不过不建议使用。
  • int (*p)(int a, int b); //也可以使用这种方式定义函数指针
  • 总结:( )里面p先和*结合,说明p是一个指针,指向的是一个函数,指向的函数参数是int类型,函数返回值为int类型。
  • 例子:
int test(int x, int y)
{
    int z = 0;
    z = x + y;
    return z;
}

int main()
{
    int a = 10;
    int b = 20;

    /*普通函数调用*/
    //printf("%d\n" ,test(a,b));    

    /*定义函数指针*/
    //int (*pa)(int,int) = test;    //写法一:定义函数指针时,参数可以填可以不填
    int (*pa)(int a,int b) = test;  //写法二:把test函数的地址存放到pa函数指针变量里面。

    /*调用函数指针*/
    printf("%d\n" ,(*pa)(a,b));     //写法一:调用函数的时候解引用也可以。
    printf("%d\n" ,pa(a,b));     	//写法二:调用函数的时候,不用解引用也可以。
    return 0;
}
#include <stdio.h>
void my_printf(char *str)
{
    printf("%s\n" ,str);
}

int main()
{
    void (*pc)(char *) = my_printf;
    pc( "hello world");    //调用的时候不解引用也可以
    return 0;
}

两段有趣的代码:

出自《C陷阱和缺陷》一书
(* (void (*)()) 0) (); //代码一
void (signal(int , void()(int))) (int); //代码二

(* (void (*)()) 0) ();      //((void (*) ())0)这段就是把0强制类型转换成:void (*) ()函数指针类型   0就是一个函数的地址。
                            //(*-找到0这个函数的地址,并调用该函数) (函数参数void)
                            //总结来说:这段代码就是一次函数调用。

void (*signal(int , void(*)(int))) (int);    //signal是一个函数声明,它有两个参数,第一个参数是int类型,第二个参数是void(*)(int)类型
                                            //第二个参数是一个函数指针,函数参数类型是int的,函数返回值是void。
                                            //signal函数的返回类型是void(*  )(int),也就是函数指针。函数参数是int类型,返回类型是void
                                            //总结:这是一次函数声明

//代码二简化
typedef unsigned int    uint;   //正常来说,我们是这样重命名的。
typedef void(*pfun_t)(int);     //但现在这个类型的名字要靠近*。给这个类型重新起一个名字叫pfun_t 
pfun_t(*signal(int , pfun_t));  //简化后的写法

2、函数指针数组

数组是一个存放相同类型数据的存储空间,我们知道指针数组是这样的。
比如:

  • int* arr[10];//数组的十个元素,每个元素是int*。

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int(* parr[10])();
* parr先和[ ]结合,说明parr是数组,数组的内容是十个int(*)()类型的函数指针。
函数指针数组的用途:转移表

练习:

写一个函数指针和函数指针数组

char* my_strcpy(char* dest ,const char* src);
char* my_strcpy1(char* dest ,const char* src);
char* my_strcpy2(char* dest ,const char* src);
char* my_strcpy3(char* dest ,const char* src);

/*1、写一个函数指针pt。能够指向my_strcpy*/
char* (*pt)(char* ,const char*) = my_strcpy;	//函数指针的定义以及初始化

/*2、写一个函数指针数组,能够存放4个my_strcpy*/
//函数指针数组的初始化以及定义
char* (*ptarr[4])(char* ,const char*) = {my_strcpy,my_strcpy1,my_strcpy2,my_strcpy3};

例子:计算器(不用函数指针数组实现)
#include <stdio.h>

void menu()
{
    printf("----------------menu-----------------\n");
    printf("---------1、Add        2、Sub--------\n");
    printf("---------3、Mul        4、Div--------\n");
    printf("---------------0、Exit---------------\n");
}

int Add(int x, int y)   //加
{
    return x + y;
}

int Sub(int x, int y)	//减
{
    return x - y;
}

int Mul(int x, int y)	//乘
{
    return x * y;
}

int Div(int x, int y)	//除
{
    return x / y;
}

int main()
{
    int input = 0;
    int x, y = 0;
    do
    {
        menu();     //打印出菜单
        printf("请选择\n");
        scanf("%d",&input);

        switch (input)
        {
        case 1:
            printf("请输入数字\n");
            scanf("%d%d",&x,&y);
            printf("%d\n",Add(x ,y));
            break;
        case 2:
            printf("请输入数字\n");
            scanf("%d%d",&x,&y);
            printf("%d\n",Sub(x ,y));
            break;
        case 3:
            printf("请输入数字\n");
            scanf("%d%d",&x,&y);
            printf("%d\n",Mul(x ,y));
            break;
        case 4:
            printf("请输入数字\n");
            scanf("%d%d",&x,&y);
            printf("%d\n",Div(x ,y));
            break;
        case 0:
            printf("退出程序\n");
            break;
        default:
            printf("输入错误,请重新输入\n");
            break;
        }
    }while (input);     //选择非0,循环会继续。
    
    return 0;
}

结果:
在这里插入图片描述

总结:
不用所谓的函数指针数组,这样其实也可以实现。那么如果我们switch里面不仅仅只有加减乘除,还有添加其它功能,那么我们还是要往里面写很多代码?这样case语句就会越来越长,越来越繁杂。

例子:计算器(使用函数指针数组的形式来实现)
#include <stdio.h>

void menu()
{
    printf("----------------menu-----------------\n");
    printf("---------1、Add        2、Sub--------\n");
    printf("---------3、Mul        4、Div--------\n");
    printf("---------5、Xor        0、Exit--------\n"");
}

int Add(int x, int y)   //加
{
    return x + y;
}

int Sub(int x, int y)	//减
{
    return x - y;
}

int Mul(int x, int y)	//乘
{
    return x * y;
}

int Div(int x, int y)	//除
{
    return x / y;
}

int Xor(int x, int y)	//这里再加一个异或
{
    return x ^ y;
}

int main()
{
    int input = 0;
    int x, y = 0;
    do
    {
        menu();     //打印出菜单
        printf("请选择\n");
        scanf("%d",&input);
        int (*PfArr[])(int ,int) = {0 ,Add ,Sub ,Mul ,Div ,Xor};    //写一个函数指针数组存放四个函数的地址
        if (input >= 1 && input <= 5)
        {
            printf("请输入数字\n");
            scanf("%d%d",&x,&y);
            printf("%d\n" ,PfArr[input](x ,y));    //通过得到数组对应的下标,从而选择调用哪个函数。
        }
        else if (input == 0)
        {
            printf("退出程序\n");
        }
        else
        {
            printf("输入错误,请重新输入\n");
        }
        
    }while (input);     //选择非0,循环会继续。
    
    return 0;
}

输出结果:
在这里插入图片描述

总结:
对比上面常规写法,下面使用函数指针数组,我们把几个函数的地址存放到一个数组里面,通过input选择的值作为数组下标,找到这个元素,直接调用这个元素作为地址的这个函数,减少了编写代码时工作量。从代码执行效率上来说,也比case语句高。假如多个函数中均要作如此处理,函数指针数组更能体现出它的优势。
经过改造后的代码,代码量变少,变得更好维护。(下面使用函数指针数组没有使用switch_case语句)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值