当前位置: 首页 > news >正文

【C语言入门级教】函数指针变量

文章目录

  • 1.函数指针变量
    • 1.1 函数指针变量的创建
    • 1.2 函数指针变量的使⽤通过函数指针调⽤指针指向的函数。
    • 1.3 两段有趣的代码
      • 1.3.1 typedef关键字
  • 2. 函数指针数组
  • 3.转移表

1.函数指针变量

1.1 函数指针变量的创建

什么是函数指针变量?

根据整型指针,数组指针,可以类⽐得出结论: 函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。

那么函数是否有地址呢? 我们做个测试:

#include <stdio.h>  
void test() 
{ printf("hehe\n"); 
}
int main() 
{ printf("test: %p\n", test); printf("&test: %p\n", &test); //两种写法都是函数的地址,建议使用这种,因为能帮助我们理解return 0; 
}

输出结果如下:

test: 005913CA 
&test: 005913CA 

确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过&函数名的⽅式获得函数的地址

如果我们要将函数的地址存放起来,就得创建函数指针变量,函数指针变量的写法其实和数组指针⾮常类似。如下:

void test() 
{ printf("hehe\n"); 
} 
void (*pf1)() = &test; 
void (*pf2)()= test;//pf1和pf2都是函数指针变量 
int Add(int x, int y) 
{ return x+y; 
} 
int(*pf3)(int, int) = Add; 
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的 

函数指针类型解析:

int    (*pf3)    (int x, int y)  |        |       ------------  |        |            | |        |      pf3指向函数的参数类型和个数的交代 |   函数指针变量名 pf3指向函数的返回类型 
int ( * ) (int x, int y) //pf3函数指针变量的类型

1.2 函数指针变量的使⽤通过函数指针调⽤指针指向的函数。

#include <stdio.h>   
int Add(int x, int y) 
{ return x+y; 
} 
int main() 
{ int(*pf3)(int, int) = Add;//相当于pf=add  printf("%d\n", (*pf3)(2, 3));//对函数指针变量解引用后传参 printf("%d\n", pf3(3, 5));//因为pf3=add,可以直接调用return 0; 
} 

输出结果:

5 
8

1.3 两段有趣的代码

代码1

(*(void ( * )())0)();
//void(*)()是一个函数指针类型,这个指针指向的函数没有参数,返回类型是void
//(void ( * )())0这段代码是在将0强制转换为void(*)()这种函数指针类型,意味着0地址处有这么一个函数
//(*(void ( * )())0)()对0地址进行解引用,去调用0地址处的这个函数

代码2

浅浅的补充一个知识:函数有三种用法:函数调用,函数声明,函数定义

void (*signal(int , void(*)(int)))(int);
//这段代码没有参数,所以不是函数调用,也没有大括号,所以不是函数定义因此这段代码是是一次函数声明
//声明的这个函数名字是signal
//signal函数有2个参数,第一个参数是int类型
//第二个参数是一个函数指针类型void(*)(int),该函数指针指向的函数的参数是int,返回类型是void
//signal函数的返回类型也是一个函数指针类型void(*)(int),该函数指针指向的函数的参数是int,返回类型是void
//=void (*)(int) signal((int , void(*)(int))但不能这样写

两段代码均出自:《C陷阱和缺陷》这本书

1.3.1 typedef关键字

typedef是⽤来类型重命名的,可以将复杂的类型简单化。

⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤:

typedef unsigned int uint; 
//将unsigned int 重命名为uint  

如果是指针类型能否重命名呢?

其实也是可以的,⽐如,将 int* 重命名为 ptr_t ,这样写:

typedef int* ptr_t;
//注意:
int* p1,p2;//p1的类型是int*,而p2的类型是int
ptr_t p3,p4;//p3和p4的类型都是int*

但是对于数组指针和函数指针稍微有点区别: ⽐如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

typedef int(*parr_t)[5]; //新的类型名必须在*的右边  

函数指针类型的重命名也是⼀样的,⽐如,将 void( * )(int) 类型重命名为 pf_t ,就可以这样写:

typedef void(*pfun_t)(int);//新的类型名必须在*的右边

那么要==简化代码2==,可以这样写:

typedef void(*pfun_t)(int); 
pfun_t signal(int, pfun_t);

举例:

int add(int x,int y)
{return x+y;
}
typedef int(*pf_t)(int,int);
int main()
{//int (*pf)(int,int)=&add;pf_t pf=&add;return 0;
}

再利用代码2演示函数的三种用法

typedef void(*pfun_t)(int);
//函数定义
pfun_t signal(int n,pfun_t p)
{//...return p;//p是函数指针
}
int main()
{pfun_t signal(int,pfun_t);//函数声明,函数声明可以位于main函数之前或之内,只要在调用语句之前即可signal(100,&test);//函数调用return 0;
}

2. 函数指针数组

数组是⼀个存放相同类型数据的存储空间,我们已经学习了指针数组, ⽐如:

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

那要把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[3])();
int *parr2[3](); 
int (*)() parr3[3];

答案是:parr1

parr1 先和 [] 结合,说明parr1是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。

3.转移表

函数指针数组的⽤途:转移表

举例:计算器的⼀般实现:

#include <stdio.h>   
int add(int a, int b) 
{ return a + b; 
} 
int sub(int a, int b) 
{ return a - b; 
} 
int mul(int a, int b) 
{ return a * b; 
} 
int div(int a, int b) 
{ return a / b; 
} 
int main() 
{ int x, y; int input = 1; int ret = 0; do {printf("*************************\n"); printf(" 1:add 2:sub \n"); printf(" 3:mul 4:div \n"); printf(" 0:exit \n"); printf("*************************\n"); printf("请选择:"); scanf("%d", &input); switch (input) { case 1: printf("输⼊操作数:"); scanf("%d %d", &x, &y); ret = add(x, y); printf("ret = %d\n", ret); break; case 2:printf("输⼊操作数:"); scanf("%d %d", &x, &y); ret = sub(x, y); printf("ret = %d\n", ret); break; case 3: printf("输⼊操作数:"); scanf("%d %d", &x, &y); ret = mul(x, y); printf("ret = %d\n", ret); break; case 4: printf("输⼊操作数:"); scanf("%d %d", &x, &y); ret = div(x, y); printf("ret = %d\n", ret); break; case 0: printf("退出程序\n"); break; default: printf("选择错误\n"); break; } }while (input); return 0; 
}

使⽤函数指针数组的实现:

#include <stdio.h>   
int add(int a, int b) 
{ return a + b; 
} 
int sub(int a, int b) 
{ return a - b; 
} 
int mul(int a, int b) 
{ return a*b; 
} 
int div(int a, int b) 
{ return a / b; 
} 
int main() 
{ int x, y; int input = 1; int ret = 0; int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表  do { printf("*************************\n"); printf(" 1:add 2:sub \n"); printf(" 3:mul 4:div \n"); printf(" 0:exit \n"); printf("*************************\n"); printf( "请选择:" ); scanf("%d", &input); if ((input <= 4 && input >= 1)) { printf( "输⼊操作数:" ); scanf( "%d %d", &x, &y); ret = (*p[input])(x, y); printf( "ret = %d\n", ret); } else if(input == 0) { printf("退出计算器\n"); } else { printf( "输⼊有误\n" );  }}while (input); return 0; 
} 
http://www.dtcms.com/a/325464.html

相关文章:

  • 当 WAF 遇上黑客——一次混合式攻击的应急复盘
  • GO学习记录三
  • 5G 非地面网络(NTN)最专业的方案
  • Docker 详解(保姆级安装+配置+使用教程)
  • 【MySQL✨】服务器安装 MySQL 及配置相关操作
  • 零 shot 语义+在线闭环:深度学习让机器人学会“主动”
  • JavaWeb-后端Web实战(IOC + DI)
  • 第八篇:交互入门:鼠标拾取物体
  • TRS(总收益互换)系统架构设计:多市场交易的技术实现分析
  • 网络编程~
  • 套接字技术、视频加载技术、断点续传技术
  • 前端执行上下文(简版)
  • 2025.8.6 图论(1)Solution
  • 大模型提示词工程实践:聊天机器人定制与实践-打造个性化任务助手
  • Oracle 19C 配置TAF
  • 投资光伏怕成本超标?鹧鸪云系统配置最优方案
  • 微算法科技(NASDAQ:MLGO)通过蚁群算法求解资源分配的全局最优解,实现低能耗的区块链资源分配
  • 【深度学习计算性能】03:自动并行
  • Apache Ignite 生产级的线程池关闭工具方法揭秘
  • 【C++】封装哈希表模拟实现unordered_set和unordered_map
  • 【10】微网优联——微网优联 嵌入式技术一面,校招,面试问答记录
  • 【Linux让旧电脑重获新生的奇妙魔法】
  • 【k8s】k8s安装与集群部署脚本
  • Godot ------ 平滑拖动03
  • 量子神经网络:从NISQ困境到逻辑比特革命的破局之路
  • Day 37:早停策略和模型权重的保存
  • C语言指针(四):字符指针、数组指针与函数指针的实战指南
  • Unity大型场景性能优化全攻略:PC与安卓端深度实践 - 场景管理、渲染优化、资源调度 C#
  • 在Mac 上生成GitLab 的SSH 密钥并将其添加到GitLab
  • 在 .NET Core 5.0 中启用 Gzip 压缩