学习心得:函数指针和函数指针数组
函数指针:指向函数的指针
引例
设有以下代码段:
#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;
}
实例:计算器(转移表版)
(未完待续)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具