首页 > 其他分享 >带你深入了解指针(2)

带你深入了解指针(2)

时间:2023-01-27 20:00:40浏览次数:35  
标签:函数 int void 了解 深入 printf pf 函数指针 指针

本篇文章继续与大家讲讲指针的一些知识,不过呢,在讲之前,分析一下以下两串代码,简单的检测一下我们的学习效果。

检测

第一串代码:(* ( void(*)() ) 0 )()

(* ( void(*)() ) 0 )()
//这行代码是函数的调用
//调用的是0地址处的函数
//为什么呢?让我们来分析一下
//首先,void(*)()是一个函数指针,而( void(*)() )则是把0强制转换成,
//类型为void(*)()的函数指针
//紧接着,*( void(*)() )0表示对0进行解引用拿到0地址处的函数,最后就是调用

第二串代码:void(*signal( int,void(*)(int) ) )(int)

void(*signal( int,void(*)(int) ) )(int);
//这行代码是对函数进行声明
//声明的函数的名字是signal
//为什么呢?同样的,我们一起分析一下
//首先,signal先与后面的括号结合(*的优先级小于括号),
//得到signal( int,void(*)(int)
//这很明显是一个函数,函数的参数有两个,一个参数是整型,另一个参数是函数指针
//函数的参数有了,是不是缺一个返回值,我门把函数的名字和参数去掉
//得到void(*)(int),这表示该函数的返回值是一个函数指针,该指针指向的函数
//返回类型是void,参数是int。

好了,小小的检测完了。我继续讲讲指针一些知识。

函数指针数组

我们已经学习了函数指针,本篇文章,我们来学习一下函数指针数组。函数指针数组怎么定义呢?我们就以函数Add为例先定义一个函数指针。

//函数Add
void Add(int)
{;}
//首先,取出Add的地址,赋给pf变量
pf=&Add;
//pf是一个指针,加上*来表示
*pf=Add;
//该指针指向的函数类型是void (int),由于*的优先级小于括号
//所以,我们不能直接这样写void *pf (int),这样会使pf先与后面的括号结合,
//就不是指针了,变成了一个函数,所以,我们要加上括号把*pf括起来
void(*pf)(int);//这样才是正确的书写
//现在我们需要一个容纳10个元素的数组,之前说过,数组的名字加方括号大小就表示
//一个数组,所以,pf[10]就表示一个容纳10个元素的数组,而我们的数组要存放
//的元素类型是void (*) (int) ,所以,就给数组加上相应的类型,就得出我们的
//对应的函数指针数组了
void (*pf[10])(int); //也就是在pf后面加上[10],让pf与[10]先结合形成数组
//而数组中每一个元素的类型是void(*)(int)

好了,我们函数指针数组的定义就讲完了,不知道我有没有讲明白,欢迎在评论区留言,接下来,我们讲讲函数指针数组的指针。

函数指针数组的指针

函数指针既然有数组,那么它也会有与之对应的指针。那它该如何去定义呢?接下来,让我们一起去了解一下。

//首先,先定义一个函数指针数组
void(*pf[10])(int);
//同样的操作,取出pf的地址,赋给变量ppf
ppf=&pf;
//它是一个指针,所以,加上*号来表示
*ppf=&pf;
//该指针指向的空间所储存的数据的类型是void(* [])(int),所以,加上这个类型
//就得到了我们的函数指针数组的指针
void(*(*ppf)[10])(int);
//学会了之后,我就直接在原基础上改造就好了,给void(*pf[10])(int)中的pf
//加上个p,再加上一个*,最后用括号括起来就得出结果了。方法有很多种,用合适
//自己的方法,敲正确的代码。以上两种方法,仅供参考。

说了这么多,我们来补充一下函数指针数组的用途:

函数指针数组的用途

//我们先简单看看以下这串简易计算器的代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
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 menu()
{
printf("********************\n");
printf("****1.add 2.sub****\n");
printf("****3.mul 4.div****\n");
printf("****0.exist ****\n");
printf("********************\n");
printf("请输入:>");
}
int main()
{
int n,a,b,ret;

do
{
menu();
scanf("%d", &n);
switch (n)
{
case 1:
printf("请输入两个操作数:>");
scanf("%d%d",&a,&b);
ret = Add(a, b);
printf("%d\n", ret);
break;
case 2:
printf("请输入两个操作数:>");
scanf("%d%d", &a, &b);
ret = Sub(a, b);
printf("%d\n", ret);
break;
case 3:
printf("请输入两个操作数:>");
scanf("%d%d", &a, &b);
ret = Mul(a, b);
printf("%d\n", ret);
break;
case 4:
printf("请输入两个操作数:>");
scanf("%d%d", &a, &b);
ret = Div(a, b);
printf("%d\n", ret);
break;
case 0:
printf("退出计算机\n");
break;
default:
printf("输入错误\n");
}
} while (n);
return 0;
}
//上面的程序使用了switch语句,我们再简单的看一下,我们使用函数指针数组优化后
//的代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void menu()
{
printf("***********************************\n");
printf("*** 1.add 2.sub ***\n");
printf("*** 3.mul 4.div ***\n");
printf("*** 0.exist ***\n");
printf("***********************************\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 a, b, n, ret;
int(*calc[5])(int, int) = { NULL,add,sub,mul,div };
do
{
menu();
printf("请输入:>");
scanf("%d", &n);
if (n == 0)
{
printf("推出计算器\n");
break;
}
else if (n >= 1 && n <= 4)
{
printf("请输入两个操作数:>");
scanf("%d%d", &a, &b);
ret = calc[n](a, b);
printf("%d\n", ret);
}
else
printf("输入错误\n");
} while (n);
return 0;
}
//这两个程序这样看,没有明显的差距,但当功能的数量不断增加时,switch语句的
//劣势就凸显出来了,代码十分冗长,而第二个只需修改部分地方。所以,我们的函数
//指针数组可不是摆设噢。
//在第二个程序中,函数指针数组起到了一个跳板的作用,我通过数组下标找到对应的
//函数指针,再通过函数指针去调用函数。所以,我们也把函数指针数组称为转移表

接下来,我们讲讲回调函数

回调函数

回调函数就是通过一个函数指针调用的函数。当我们把一个函数的指针作为参数传给一个函数,而这个函数在使用的时候,通过函数指针调用的函数,我们就称之为会回调函数。

什么意思呢?让我们走进代码中去看看:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
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 menu()
{
printf("**************************************\n");
printf("**** 1.add 2.sub ****\n");
printf("**** 3.mul 4.div ****\n");
printf("**** 0.exist ****\n");
printf("**************************************\n");
}
void calc(int (*pf)(int, int))
{
int a, b, ret;
printf("请输入两个操作的数字:>");
scanf("%d%d", &a, &b);
ret = pf(a, b);
printf("%d\n", ret);
}
int main()
{
int input;
do
{
menu();
printf("请输入:>");
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出计算器\n");
break;
case 1:
calc(add);
break;
case 2:
calc(sub);
break;
case 3:
calc(mul);
break;
case 4:
calc(div);
break;
default:
printf("输入错误\n");
}
} while (input);
return 0;
}
//以上程序把函数的地址传给了函数calc,函数calc通过该函数指针调用了相应的函数,
//相应的函数就称之为回调函数。比如函数calc通过add这个地址调用了函数add,那么
//函数add就称之为回调函数

好了,本次分享就到此结束了,不知道我有没有说明白,给予你一点点收获。如果你有所收获,别忘了给我点个赞,这是对我最好的回馈,当然你也可以在评论发表一下你的收获和心得,亦或者指出我的不足之处。如果喜欢我的分享,别忘了给我点关注噢。

标签:函数,int,void,了解,深入,printf,pf,函数指针,指针
From: https://blog.51cto.com/u_15933803/6024263

相关文章

  • 【双指针】LeetCode 680. 验证回文串 II
    题目链接680.验证回文串II思路题目允许删除一个字符,那么当我们判断到一对字符不相等时,可以分别判断区间\([left+1,right]\)和区间\([left,right-1]\)是否能......
  • 一分钟带你了解mySql执行SQL的内部原理
    1、把MySQL当个黑盒子一样执行SQL语句我们知道执行了insert语句之后,在表里会多出来一条数据;执行了update语句之后,会对表里的数据进行更改;执行了delete语句之后,会把表里的......
  • 【双指针】LeetCode 125. 验证回文串
    题目链接125.验证回文串思路简单双指针应用代码classSolution{publicbooleanisPalindrome(Strings){StringBuffersgood=newStringBuffer();......
  • LeetCode三数之和(vector/排序+双指针)
    原题解题目约束解法classSolution{public:vector<vector<int>>threeSum(vector<int>&nums){intn=nums.size();sort(nums.begin......
  • 指针
    回调函数packagemainimport"fmt"//把匿名函数作为另一个函数的参数,就叫做回调函数//设计一个函数,求2个数的加减乘除funcadd(a,bint)int{//add:func(in......
  • LeetCode盛最多水的容器(/双指针)
    原题解题目约束题解classSolution{public:intmaxArea(vector<int>&height){intl=0,r=height.size()-1;intans=0;......
  • 力扣 264. 丑数 II [堆;多指针]
    264.丑数II给你一个整数 n ,请你找出并返回第 n 个 丑数 。丑数 就是只包含质因数 2、3 和/或 5 的正整数。示例1:输入:n=10输出:12解释:[1,2,3,......
  • C++中的指针和引用
    指针在C++,指针本质上也是一个对象,它存储的是对象的地址,而非值本身。是一个有趣且功能强大的特性。指针的定义指针的定义,使用"*"进行修饰一个变量。int*p1,*p2如上,定......
  • 精华推荐 | 【深入浅出 RocketMQ原理及实战】「底层源码挖掘系列」透彻剖析贯穿Rocket
    精华推荐|【深入浅出RocketMQ原理及实战】「底层源码挖掘系列」透彻剖析贯穿RocketMQ的消费者端的运行核心的流程上篇:分析对应总体消费流程的判断和校验以及限流控制和回......
  • 派生类指针赋值给基类指针
    #include<iostream>usingnamespacestd;//基类AclassA{public:A(inta);public:voiddisplay();protected:intm_a;};A::A(inta):m_a(a){}voidA::displa......