C语言——函数指针

目录

1. 函数指针概念

1.1 函数指针的声明

1.2 函数指针的定义

1.3 使用typedef定义函数指针的别名

1.4 将常数转换为函数指针

1.5 函数指针的调用

1.6 将函数指针作为函数的传入参数

2. 简单的例子


1. 函数指针概念

1.1 函数指针的声明

类似变量在内存中会分配一个空间,函数在内存中也会分配一个空间,这个空间的入口(或者叫首地址)称为函数的地址。用整型指针可以保存整形变量的地址,同样地,用函数指针可以保存函数的地址。

我们回顾一下怎么定义一个整型指针:

int *p_int;

由于整型变量只需要定义变量的类型即可完全规定这个变量的格式(个人理解,变量声明限定了变量的格式,变量定义规定了其内容)。但是对于一个函数来说(将函数也看成一种复杂的变量),函数的格式由哪些东西唯一确定呢?函数返回值,函数参数个数和参数类型

那么我们是不是可以通过函数返回值,函数参数个数和参数类型来声明函数指针变量的格式?

int (*p_int);//声明了一个整型指针变量
int (*p)(int,int);//声明了一个函数指针变量,该变量指向一个函数,该函数的返回值为int类型,并且接受两个int类型的形参。

上面的代码声明了一个函数指针,将其与整型指针类比,是不是可以理解为:由于函数的格式的确定需要返回值,参数个数以及参数类型这些东西唯一确定,那么声明一个函数指针就需要这三个东西来唯一确定函数指针的格式

1.2 函数指针的定义

下面来看看,已经声明了函数指针,怎么定义这个函数指针。

类似的,还是用整形变量的定义来类比。

int *p_int;    //声明一个整型指针变量
int arr[2]={1,2};//定义一个整型数组
*p_int=arr;    //整型指针变量的定义(赋值)

不过这次用的是整形数组,见上面的代码。整型数组arr[],其名称代表数组的入口,因此*p_int=arr; 可以给整型指针变量赋值,赋值的过程相当于规定了变量的内容,上面1.1中声明的过程相当于规定了变量的格式,变量的格式和内容都规定了,就可以使用这个变量了。此时通过p_int这个指针就可以访问arr数组。

int Max(int x, int y);//声明max函数
int main(void){
    int (*p)(int,int);  //定义函数指针
    p=Max;              //类似的,函数名也代表函数的入口(函数地址),可以用其初始化函数指针,但是前提是函数指针的格式与用来初始化的函数的格式相同
    return 0;
}

上面的代码先定义了一个函数指针,其指向一个函数,该函数的返回值是int,并且传入参数为两个int类型的参数。而Max函数本身就是返回值是int,并且传入参数为两个int类型的参数的函数,因此可以用Max函数初始化函数指针p。而函数名Max就是函数的入口,因此p=Max就对函数指针p进行了初始化。

1.3 使用typedef定义函数指针的别名

我们都知道使用typedef关键字可以定义数据类型的别名,最常见的是给结构体定义别名,如下:

//嵌入式开发中最常见的使用typedef定义unsigned int的别名为uint32_t
typedef unsigned  int uint32_t;

//定义node结构体,Node是其别名
typedef struct node{
    int val;
    struct node* next;
}Node;
typedef Node* pNode;//定义Node*类型的别名为pNode
pNode head;//使用类型的别名声明struct node*类型的变量;

同样地,使用typedef关键字可以定义函数指针的别名,如下:

//HANDLER是函数指针类型的别名,该类型的函数指针指向一个返回值为int,参数为两个int的函数
typedef int (*HANDLER)(int,int);

HANDLER表示一个函数指针,指向一个返回值是int,参数为两个int类型的函数。

使用HANDLER可以定义函数指针类型的变量,

typedef int (*HANDLER)(int,int);//定义一个函数指针类型的别名
int Max(int x, int y);//声明max函数

int main(void){

    int (*p)(int,int);  //定义函数指针
    HANDLER q;//使用函数指针的别名声明q这个函数指针
    q=Max;
    p=Max;  //给函数指针赋值(其值就是函数名)
    int res1=(*p)(a,b);  //赋值完成之后,通过函数指针调用函数
    int res2=q(a,b);
    printf("res1:%d; res2:%d ",res1,res2);   //打印res1和res2的值,就是a和b的最大值,1
}

1.4 将常数转换为函数指针

嵌入式开发经常会遇到的问题,将一个常数,比如0x0001转换为一个“指向返回值为void的函数的指针”,我们先看看一个指向返回值为void的函数的指针,这种类型的变量怎么定义。

void (*p)();//定义了一个函数指针p,其指向一个返回值为void的函数

然后由于常数0x0001不是指针类型的变量,我们先需要强制类型转换,将常数类型转为函数指针类型,如下:

(void (*)())0x0001;//将0x0001强制类型转换为一个函数指针,其指向一个返回值为void类型的函数

这个时候我们成功将常数0x0001转为了函数指针。如果想让程序跳转到0x0001的地方执行,那么只需要通过函数指针调用函数就行了,如下:

(void (*)())0x0001;//将0x0001强制类型转换为一个函数指针,其指向一个返回值为void类型的函数
(*(void (*)())0x0001)();//函数指针前加“*”,解引用,然后调用函数

结合上一节说的typedef关键字,可以将上面两行代码写成如下的形式:

typedef void (*funcptr)();//定义函数指针类型的别名
(funcptr)0x0001;//使用funcptr将0x0001强制类型转换为一个函数指针
((*funcptr)0x0001)();//函数指针前加“*”,解引用,然后调用函数

1.5 函数指针的调用

知道了如何定义和声明函数指针,下面就是如何使用函数指针了。这里还是用整型数组的使用来类比。

int *p_int;    //声明一个整型指针变量
int arr[2]={1,2};//定义一个整型数组
*p_int=arr;    //整型指针变量的定义(赋值)
printf("%d",(*p_int)[0]);//该行代码打印(*p_int)[0],就是arr[0],即arr数组第一个元素

通过上面的代码可以看出,使用“*+指针名”可以达到与使用数组名一样的访问数组元素的目的。那么,看看下面的代码:

int Max(int x, int y);//声明max函数
int main(void){
    int a=0, b=1;
    int (*p)(int,int);  //定义函数指针
    p=Max;              //类似的,函数名也代表函数的入口(函数地址),可以用其初始化函数指针,但是前提是函数指针的格式与用来初始化的函数的格式相同
    int res=(*p)(a,b);//通过*+函数变量名,可以访问p指向的函数,与res=Max(a,b)相同
    return 0;
}

使用“*+指针名”可以通过函数指针访问该指针指向的那个函数。是不是跟数组的访问很类似?如果把函数也当成变量(只不过这个“变量”,其结构有比较多的因素决定,其内容可以占据很大的空间),那么这样的函数指针调用是不是就更容易理解了。

1.6 将函数指针作为函数的传入参数

有了函数指针之后,是不是就可以将函数指针作为参数传递给另一个函数?我们给函数传递参数的目的是什么?为了使用外部的数据来改变函数的操作或者调用逻辑,影响函数的返回值或者通过传入变量的指针,在函数中通过指针修改变量的内容。

函数指针作为函数的传入参数,可以给函数传递一个函数供其内部调用,这个传入的函数,我们可以在外部完成函数的实现,这样可以在保证主逻辑不变的情况下,通过改变传入函数的逻辑,最终影响主逻辑的输出

int Max(int x, int y);//声明max函数
int GetValue(int x,int y,int(*p)(int,int));//声明getvalue函数
int main(void){
    int a=1,b=0;        //定义两个变量a,b
    int maxValue=GetValue(a,b,Max);//将函数指针作为参数传递给函数
    return 0;
}

//这个函数通过函数指针p调用它的传入参数(p指向的函数的入口)
int GetValue(int x,int y,int(*p)(int,int)){
    return (*p)(x,y);//通过函数指针,调用传入的函数参数,得到函数参数的返回值,然后返回该函数的返回值
}
//定义max函数,该函数返回两个变量中较大的那个变量的值
int Max(int x, int y){
    return x>y?x:y;
}

这样将外部定义的函数通过函数指针传递给内部函数,可以在满足一定内部算法封装的情况下,通过外部定义的函数来改变算法的具体输出形式。比如C标准库的qsort排序算法,需要我们自己定义比较函数,来决定排序的结果是升序还是降序

2. 简单的例子

举一个简单的例子,该例子先声明一个函数指针,然后定义该函数指针,最后通过函数指针访问该指针指向的函数,实现函数调用的功能

#include <stdio.h>
#include <stdlib.h>
//声明max函数
int Max(int x, int y);
int main(void){

    int a=1,b=0;        //定义两个变量a,b
    int *q;
    int (*p)(int,int);  //定义函数指针
    p=Max;  //给函数指针赋值(其值就是函数名)
    int res=(*p)(a,b);  //赋值完成之后,通过函数指针调用函数
    printf("%d",res);   //打印res的值,就是a和b的最大值,1
    return 0;
}
//定义max函数,该函数返回两个变量中较大的那个变量的值
int Max(int x, int y){
    return x>y?x:y;
}

下面是通过给函数传递函数指针,在函数中调用外部定义的函数的例子。

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

int Max(int x, int y);//声明max函数
int Min(int x, int y);//声明min函数
int GetValue(int x,int y,int(*p)(int,int));//生命getvalue函数
int main(void){

    int a=1,b=0;        //定义两个变量a,b
    int maxValue=GetValue(a,b,Max);
    int minValue=GetValue(a,b,Min);

    printf("The smaller value between a and b is:%d\n",minValue);   //打印res的值,就是a和b的最大值,1
    printf("The larger value between a and b is:%d\n",maxValue);   //打印res的值,就是a和b的最大值,1

    return 0;
}
//这个函数通过函数指针p调用它的传入参数(p指向的函数的入口)
int GetValue(int x,int y,int(*p)(int,int)){
    return (*p)(x,y);//通过函数指针,调用传入的函数参数,得到函数参数的返回值,然后返回该函数的返回值
}
//定义max函数,该函数返回两个变量中较大的那个变量的值
int Max(int x, int y){
    return x>y?x:y;
}
//定义max函数,该函数返回两个变量中较小的那个变量的值
int Min(int x, int y){
    return x<y?x:y;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值