指针进阶篇(2)

news/2024/11/24 13:53:52/

进阶指针

 🤔前言🤔

一、😊函数指针😊

二、😜函数指针数组😜

三 、😝指向函数指针数组的指针😝

四、🌝回调函数🌝

🍀小结🍀


🎉博客主页:小智_x0___0x_

🎉欢迎关注:👍点赞🙌收藏✍️留言

🎉系列专栏:C语言进阶篇

🎉代码仓库:Xiaozhi_c

        家银们更新不易,你们的👍点赞👍和👉关注👈真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!

 

 🤔前言🤔

前面我们学了进阶指针(1),熟悉并了解了字符指针、指针数组、数组指针、数组参数、指针参数,今天我们再来探讨指针。

本章重点:

  1. 函数指针
  2. 函数指针数组
  3. 指向函数指针数组的指针
  4. 回调函数

一、😊函数指针😊

函数指针是指向函数的指针变量。

先看一段代码>

#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}

输出结果> 

 结果输出的是两个地址,这两个地址都是test函数的地址。

那应该如何保存呢?

我们再来看看代码>

void test()
{
printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

首先,能存储地址,第一个要求ptun1或者ptfun2是指针,那到底那个是指针呢?

pfun1可以存放,pfun1先和*结合,说明pfun1是指针,指针指向一个函数,指向的是一个无参函数,返回值为void

我们再来看看两个有趣的代码>

//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);

代码解释>

int main()
{//该代码是一次函数调用//调用0地址处的一个函数//首先代码中将0强制类型转换为类型为void (*)()的函数指针//然后去调用0地址处的函数//( *( void (*)() ) 0 )();typedef unsigned int uint;typedef void(*pf_t)(int) ;pf_t signal(int, pf_t);void (* signal( int, void(*)(int) ) )(int);//该代码是一次函数的声明//声明的函数名字叫signal//signal函数的参数有2个,第一个是int类型,第二个是函数指针类型,该函数指针能够指向的那个函数的参数是int//返回类型是void//signal函数的返回类型是一个函数指针,该函数指针能够指向的那个函数的参数是int,返回类型是void//void (*)(int) signal(int, void(*)(int));return 0;
}

二、😜函数指针数组😜

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

比如:

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

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

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

答案是:parr1

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

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

我们来看一个简单的例子(计算器):

void menu()
{printf("*******************************\n");printf("****** 1. add   2. sub    *****\n");printf("****** 3. mul   4. div    *****\n");printf("****** 0. exit            *****\n");printf("*******************************\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 = 0;int y = 0;int ret = 0;do {menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = Add(x, y);printf("%d\n", ret);break;case 2:printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("%d\n", ret);break;case 3:printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("%d\n", ret);break;case 4:printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = Div(x, y);printf("%d\n", ret);break;case 0:printf("退出计算器\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}

我们来看运行效果>

我们可以发现switch语句中有许多冗余代码,那该如何去化解这样的问题呢?

我们再来看看使用函数指针数组的实现: 

void menu()
{printf("*******************************\n");printf("****** 1. add   2. sub    *****\n");printf("****** 3. mul   4. div    *****\n");printf("****** 0. exit            *****\n");printf("*******************************\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 (*pf[5])(int, int) = { NULL, Add, Sub, Mul, Div };int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("请选择:>");scanf("%d", &input);if (input == 0){printf("退出计算器\n");//break;}else if (input>=1 &&input<=4){printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = pf[input](x, y);printf("%d\n", ret);}else{printf("选择错误\n");}} while (input);return 0;
}

运行效果>

三 、😝指向函数指针数组的指针😝

指向函数指针数组的指针是一个指针。

指针指向一个数组,数组的元素都是函数指针

void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char*) = test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[5])(const char*) = &pfunArr;
return 0;
}

四、🌝回调函数🌝

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

还是熟悉的例子(计算器):

void menu()
{printf("*******************************\n");printf("****** 1. add   2. sub    *****\n");printf("****** 3. mul   4. div    *****\n");printf("****** 0. exit            *****\n");printf("*******************************\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;
}void calc(int (*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}int main()
{int input = 0;do {menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:calc(Add);break;case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break;case 0:printf("退出计算器\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}

我们来画图来详解一下>

🍀小结🍀

今天我们学习了函数指针、函数指针数组、指向函数指针数组的指针、回调函数,相信大家看完有一定的收获。

种一棵树的最好时间是十年前,其次是现在! 把握好当下,合理利用时间努力奋斗,相信大家一定会实现自己的目标!加油!创作不易,辛苦各位小伙伴们动动小手,三连一波💕💕~~~,本文中也有不足之处,欢迎各位随时私信点评指正!
本节课的代码已上传gitee仓库

 


http://www.ppmy.cn/news/11361.html

相关文章

结构体 · 内存对齐

欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e;&#x1f49e; 前言&#xff1a; 在初识C语言中简单介绍了结构体&#xff0c;结构体可以理解为不同类型数据的集合体&#xff0c;但是你想过结构体的大小是如何计算的吗&#xff1f;看完这篇博客&#xff0c;你就能给自己答…

测试开发基础 mvn test | 利用 Maven Surefire Plugin 做测试用例基础执行管理

一、需求在测试工作场景中&#xff0c;经常会遇到下面的问题&#xff1a;1、执行自动化测试用例的时候&#xff0c;只想指定某个测试类&#xff0c;或者某个方法&#xff0c;又或者某一类用例等&#xff0c;怎么办&#xff1f;2、想要和 Jenkins 一起进行持续集成&#xff0c;可…

java 数组看这一篇文章就够了

第一章 数组概述 数组介绍1 2 3 41. 数组是多个相同类型数据的组合 2. 数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型 3. 数组属引用类型,数组中的每个元素相当于该对象的成员变量 4. 数组中的数据是有序的,可以通过序号(索引或者下标)获取,索引从0开始数组…

米筐量化终端是什么?

米筐量化终端大家应该也能想象到是应用的终端&#xff0c;是系统执行的终端环节&#xff0c;如果是用在量化方面&#xff0c;那它就是策略定制的终端&#xff0c;是方便投资者输入量化策略执行出来发最终优质目的&#xff0c;精确到细分股票的账户成交量&#xff0c;股价以及融…

刚当上leader,我让组员去开会,他非说有更重要的会

☆ 职场上经常有那么一种情况就是组长喊组员开会&#xff0c;开周会&#xff0c;开晨会&#xff0c;开各种会&#xff0c;而更有一种常见的情况呢就是组长缺失威严&#xff0c;喊组员开会&#xff0c;组员不听话&#xff0c;说有更重要的会议&#xff0c;不想参加。 ☆ 本文将以…

Kettle的安装以及简单使用

Kettle是一款开源免费的ETL工具&#xff0c;ETL全称 Extract - Transform - Load 意味着数据抽取&#xff0c;转换&#xff0c;装载的过程。 ETL是将业务系统的数据经过抽取、清洗转换之后加载到数据仓库的过程&#xff0c;目的是将企业中的分散、零乱、标准不统一的数据整合到…

多线程案例

1.阻塞队列1.1阻塞队列的工作原理阻塞队列本质上还是一个队列&#xff0c;但是在队列的基础上加入了阻塞功能&#xff0c;并且线程安全。那么它的阻塞功能体现在两方面1.当队列为空时&#xff0c;进行出队列操作&#xff0c;就进入阻塞状态2.当队列满了时&#xff0c;进行入队列…

Flink多流转换(Flink Stream Unoin、Flink Stream Connect、Flink Stream Window Join)

文章目录多流转换1、分流操作1.1、在flink 1.13版本中已弃用.split()进行分流1.2、使用&#xff08;process function&#xff09;的侧输出流&#xff08;side output&#xff09;进行分流2、基本合流操作2.1、联合&#xff08;Flink Stream Union&#xff09;2.2、连接&#x…