学习心得:函数指针和函数指针数组

函数指针:指向函数的指针

引例

设有以下代码段:

#include<stdio.h>
 int main(){
     printf("%p",main);
     return 0;
 }

运行结果表明,真的输出了main函数的地址。

我们不妨进行类比:数组名是什么?数组名是数组的首元素的地址。

由此可得,函数名是什么?函数名实际上是函数的入口地址

因此,我们有了一些想法:是否可以用一个指针指向这个函数呢?答案是肯定的,这就是函数指针。

注:不要将函数指针(指向函数的指针)和指针函数(返回值为指针类型的函数)混淆,它们仅仅是在名字上相似,二者是截然不同的两种东西。

函数指针的定义、初始化、赋值

函数指针的定义语法:

 返回值类型 (*函数指针变量名)(形参表);
  • 函数指针的返回值类型意为该函数指针能指向该返回值类型的函数。

  • 注意指针变量定义说明符(即*)与函数指针变量名之间必须加圆括号,否则*将与返回值类型结合而成为返回值为指针类型的指针函数,而非函数指针。

  • 形参表是可选的,若要指向的函数中没有形参则缺省,仅保留圆括号即可。勿忘函数指针的形参需要与被调函数形参及实参类型一致。

    函数指针的形参表中一般缺省形参名,只需声明形参类型即可。C语言中一般只建议在定义函数指针时缺省形参名。

设已有下面的函数已定义:

 void print(int x){
     printf("%d",x);
 }

则可以定义函数指针:

 void (*fun)(int);//形参表的变量名一般省略,省略的写法一般只在函数指针时用到

函数指针可以由初始化或定义后赋值两种方式。

  • 方式1:初始化

 void (*fun)(int)=print;//注意:这里需要写成print而不是print(int),因为需要使指针指向一个函数的地址而非调用函数后的返回值
 //或写作:
 void (*fun)(int)=&print;
 //上述两种写法是等效的,后者相当于显式告诉编译器应该做什么。
  • 方式2:赋值

注:赋值操作不可在全局作用域中进行,否则会被编译器进行隐式转换导致重名报错。赋值操作应在块作用域中进行。

void (*fun)(int);
 fun=print;
 //或写作:
 void (*fun)(int);
 fun=&print;//同理,这里是在显式告诉编译器它应该怎么做。

函数指针的调用

设已有以下函数和函数指针,并在main函数中创建了实参:

#include<stdio.h>
 void print(int x){
     printf("%d",x);
 }
 void (*fun)(int)=print;
 int main(){
     int a;
     scanf("%d",&a);
     //此处应该如何调用呢?
     return 0;
 }

调用函数指针语法:

(*函数指针变量名)(实参表);
 //或写作:
 函数指针变量名(实参表);
上述两种写法是等效的,前者只是在显式地告诉编译器应该做什么。
 注:若写成前者的形式,且返回值类型不是指针类型时,解引用符(即*)必须加圆括号。否则会按照后者的写法先进行处理,得到指定的返回值类型的数据后,再对该非指针的数据类型进行解引用,会导致编译错误。

因此第9行应写作:

 (*fun)(a);
 //或:
 fun(a);

和typedef结合使用的函数指针

我们知道,typedef的常规语法如下:

 typedef 原名 新名

而typedef在与函数指针使用时,和与枚举、结构体、联合体比较类似,但又稍显不同,需加特别注意。

还是假设有以下函数已定义:

void print(int x){
     printf("%d",x);
 }

用typedef创建函数指针时,应:

 typedef void (*fun)(int);//创建了一个void(*)(int)的数据类型,将指针变量fun提取出来作为此数据类型的别名
 fun process;//定义一个名为process的fun类型变量(类比typedef和枚举、结构体、联合体的使用)
 process=print;//使名为process的函数指针变量指向print函数

调用时,调用变量process即可。调用方法与常规的函数指针调用方法一致。此处不再赘述。

函数指针做形参

#include<stdio.h>
void printdata(int x){
    printf("%d",x);
}
void printadd(int x){
    printf("(%p)",&x);
}
void fun(int x,void (*f)(int)){
    f(x);
}
int main(){
    int a=100;
    fun(a,printdata);
    fun(a,printadd);
    return 0;
}

函数指针数组:批量组织同种函数指针类型

只要会写普通指针数组,那么运用类比推理思想即可写出函数指针数组,难度较低,在此不再赘述。

#include<stdio.h>
 void print(int x){
     printf("%d",x);
 }
 void pradd(int x){
     printf("(%p)",&x);
 }
 int main(){
     void (*fun[2])(int)={print,pradd};
     int a=100;
     for(int i=0;i<2;i++){
         fun[i](a);
     }
     return 0;
 }
  • typedef与函数指针数组结合使用

#include<stdio.h>
void printdata(int x){
    printf("%d",x);
}
void printadd(int x){
    printf("(%p)",&x);
}
int main(int arg,char *argv[]){
    int x=100;
    typedef void(*p)(int);
    p fun[2]={printdata,printadd};
    for(int i=0;i<2;i++)
        fun[i](x);
    return 0;
}
  • 指向函数指针数组的函数指针

#include<stdio.h>
void printdata(int x){
    printf("%d",x);
}
void printadd(int x){
    printf("(%p)",&x);
}
int main(){
    int a=100;
    void (*fun[2])(int)={printdata,printadd};
    void (**f)(int)=fun;
    for(int i=0;i<2;i++){
        (*(f+i))(a);
    }
    return 0;
}

转移表

转移表简介

在C语言中,"转移表"通常指的是使用数组来实现一种跳转或分发的技术。这种技术通常用于替代一系列的if-else语句或switch语句,以提高代码的效率和可维护性。

转移表的实现

转移表的一种常见实现是通过使用函数指针数组,将一组函数关联到相应的输入值或条件上。这样可以避免使用大量的条件判断语句,提高代码的执行速度。

#include <stdio.h>
void case1() {
    printf("You chose case 1\n");
}

void case2() {
    printf("You chose case 2\n");
}

void case3() {
    printf("You chose case 3\n");
}
int main() {
    void (*functionTable[])() = {case1, case2, case3};
    int choice;
    scanf("%d", &choice);
    if (choice >= 1 && choice <= 3) {
        (*functionTable[choice - 1])(); // 注意数组索引从0开始
    } else {
        printf("Invalid choice\n");
    }
    return 0;
}

实例:计算器(转移表版)

(未完待续)

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值