当前位置:网站首页>c语言进阶篇:指针(二)
c语言进阶篇:指针(二)
2022-07-28 21:28:00 【摸鱼王胖嘟嘟】
作者介绍:大家好,我是摸鱼王胖嘟嘟,可以叫我小嘟
作者主页:摸鱼王胖嘟嘟的个人博客主页.
作者的gitee: 小比特_嘟嘟的个人gitee
系列专栏: 【从0到1,漫游c语言的世界】
小嘟和大家一起学习,一起进步!尽己所能,写好每一篇博客,沉醉在自己进步的喜悦当中🤭。如果文章有错误,欢迎大家在评论区️指正。让我们开始今天的学习吧!
前言
大家好哇~今天要来接着讲指针进阶了,话不多说,让我们开始今天的学习吧!
函数指针
️函数指针定义
函数指针:指向函数的指针,用来存放函数的地址的。
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*p1)(int, int) = &Add;
/* p1是个函数指针, 用来存放函数的地址的 通过对p1进行解引用,可以找到这个函数 * - 表示p1是个指针,int(int,int)表示p1指向的这个函数的类型 指针p1的类型是int(*)(int,int) */
int (*p2)(int x, int y) = &Add;//也可以
int (*p3)(int, int) = Add;
//对于函数来说,&函数名和函数名都是函数的地址,所以这样写也可以
printf("%p\n", p1);//007010B4
printf("%p\n", p2);//007010B4
printf("%p\n", p3);//007010B4
return 0;
}
如何通过对函数指针p1解引用找到这个函数,调用这个函数呢?
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*p1)(int, int) = &Add;
int ret = (*p1)(3, 5);
//(*p1) - 对p1解引用,找到了这个函数
//(*P1)(3,5) - 给函数传参,调用这个函数
printf("%d\n", ret);//8
return 0;
}
那么之前我们是如何调用函数呢?对比如图:
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int ret = Add(3, 5);
printf("%d\n", ret);//8
return 0;
}
而且我们会发现:
在获取函数地址时,&和不加&都可以;
在通过函数名调用函数时,*和不加 *都可以。
阅读两段有趣的代码:
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
代码1解释:
这串代码可以分成3部分来看
先看绿色部分,这是一个函数指针类型,我们可以通过这个类型知道这个指针指向的函数大概是这样的:void test()
然后看红色部分,(类型)是强制类型转换,将int型的0强制类型转换成void test(*)类型,即整数0变成了函数的地址0
最后看黑色部分,(*函数的地址)(),这不就是函数的调用吗,调用的函数大概是这样的:void test()
所以,这串代码就是一次函数的调用,调用的是0作为地址处的函数。
这串代码用到的知识:
1.指针的使用
2.函数指针的类型和它指向的函数息息相关
3.强制类型转换
代码2解释:
首先我们看红色部分,signal是函数名,int是整型
void(*)(int)是函数指针类型
黑色部分 void( * )(int)也是函数指针类型
函数声明的构成:返回类型 函数名(参数类型,参数类型)
所以,以上代码是一次函数声明。
声明的signal函数的第一个参数的类型是int,第二个参数类型是函数指针void( * )int,该函数指针指向的函数参数是int,返回类型是void,signal函数的返回类型也是一个函数指针,该函数指针指向的函数参数是int,返回类型是int。
但是这样的代码可读性特别差。
因为函数指针类型void(*)(int)两次被用到,可以用typdef把类型进行重命名,
对这个函数指针类型进行重命名的格式为 typedef void( * 重命的名)(int)
上面的代码2就可以写成这样:
typedef unsigned int unit;
//typedef void(*)(int) pf_t;
//这样写是错误的
typedef void(*pf_t)(int);//这样写才对,意思是把void(*)(int)类型重命名成pf_t
#include <stdio.h>
int main()
{
pf_t signal(int, pf_t);
return 0;
}
️函数指针的应用:
#include<stdio.h>
//函数的嵌套调用
int Add(int x, int y)
{
return x + y;
}
int cal()
{
return Add(3, 5);
}
int main()
{
int ret = cal();
printf("%d\n", ret);
return 0;
}
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
//在cal函数中调用Add函数
int cal(int(*pf)(int,int))//函数的地址用函数指针来接收
{
int ret = (*pf)(3, 5);
printf("%d\n", ret);
}
int main()
{
cal(Add);
return 0;
}
2.真正的函数指针的应用(写一个简单版计算器) :
//写一个计算器
//加法、减法、乘法、除法
#include <stdio.h>
void menu()
{
printf("*******************************\n");
printf("******* 1. add *********\n");
printf("******* 2. sub **********\n");
printf("******* 3. mul **********\n");
printf("******* 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;
}
函数指针数组
函数指针数组,是个数组,是用来存放函数指针的数组。
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
//要将&a,&b,&c这三个地址放在一个数组中,需要这三个地址的类型相同。
int* pa = &a;//刚好这三个地址的类型都是int*
int* parr[3] = {
&a,&b,&c };
//parr是个数组,数组里有3个元素,每个元素的类型都是int*
return 0;
}
发现指针数组和它存放的指针只是parr[3]和pa的区别
于是得到函数指针数组:
#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;
}
int main()
{
//要将Add,Sub,Mul,Div这四个地址放到一个数组中,需要这四个地址的类型相同
int (*pf)(int, int) = Add;//刚好这四个地址的类型都是int(*)(int,int)
int (*arr[4])(int, int) = {
Add,Sub,Mul,Div };
//arr数组中有4个元素,每个元素的类型是int(*)(int,int)
return 0;
}
那么如何调用呢?
int main()
{
int (*arr[4])(int, int) = {
Add,Sub,Mul,Div };
int i = 0;
for (i = 0; i < 4; i++)
{
printf("%d\n", arr[i](3, 5));//调用函数
//通过arr[i]找到每个函数名,通过函数名调用函数
}
return 0;
}
效果如下:
️函数指针数组应用
写一个计算器:实现简单的加减乘除
用函数指针数组,大大减少了重复代码。 转移表
#include <stdio.h>
void menu()
{
printf("*******************************\n");
printf("******* 1. add *********\n");
printf("******* 2. sub **********\n");
printf("******* 3. mul **********\n");
printf("******* 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;
int (*pf_arr[])(int, int) = {
0,Add,Sub,Mul,Div };//函数指针数组是直接将不同的函数名存到了数组中,
//通过数组下标就可以访问数组中的不同函数名,从而找到对应的函数进行函数调用
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
if (input == 0)
{
printf("退出计算器\n");
}
else if ((input >= 1) && (input <= 4))
{
printf("请输入操作数:>");
scanf("%d %d", &x, &y);
ret = pf_arr[input](x, y);
printf("%d\n", ret);
}
else
{
printf("选择错误\n");
}
} while (input);
return 0;
}
为什么说大大减少了重复代码呢?
来看下面的做法:(实现一个计算器)
普通做法:(代码重复率特别高)
#include <stdio.h>
void menu()
{
printf("*******************************\n");
printf("******* 1. add *********\n");
printf("******* 2. sub **********\n");
printf("******* 3. mul **********\n");
printf("******* 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;
}
于是进行改进:
函数指针的做法:
函数名传过来用函数指针接收,
通过传过来的不同函数名,可以找到对应的函数进行函数调用
//计算
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;
}
函数指针数组的做法:
函数指针数组是直接将不同的函数名存到了数组中,
通过数组下标就可以访问数组中的不同函数名,从而找到对应的函数进行函数调用
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
int (*pf_arr[])(int, int) = {
0,Add,Sub,Mul,Div };//函数指针数组是直接将不同的函数名存到了数组中,
//通过数组下标就可以访问数组中的不同函数名,从而找到对应的函数进行函数调用
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
case 2:
case 3:
case 4:
printf("请输入操作数:>");
scanf("%d %d", &x, &y);
ret = pf_arr[input](x, y);
printf("%d\n", ret);
break;
case 0:
printf("退出计算器\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
是不是感觉函数指针和函数指针数组的做法更简单呢,大大减少了代码的重复率。
当然,下面这两部分代码的功能是一样的。
那么,我们可能会产生疑问,函数指针和函数指针数组有什么区别呢?用函数指针和函数指针数组来实现计算器又有什么区别呢?
️函数指针和函数指针数组的区别:
函数指针是指向函数的指针,里面存放的是函数的地址,可以是函数名,从而通过函数名找到对应的函数,进行函数调用。
函数指针数组是存放函数指针的数组,里面存放的是不同函数的地址,可以是不同的函数名,我们可以通过数组下标访问数组中的元素(函数名),从而通过函数名找到对应的函数,进行函数调用。
️用函数指针和函数指针数组来实现计算器的区别:
函数指针实现计算器:函数名传过来用函数指针接收,通过传过来的不同函数名,可以找到对应的函数进行函数调用
函数指针数组实现计算器:函数指针数组是直接将不同的函数名存到了数组中,通过数组下标就可以访问数组中的不同函数名,从而找到对应的函数进行函数调用
边栏推荐
- Empowering Chinese core entrepreneurs! See how Moore elite solves the development problems of small and medium-sized chip Enterprises
- 投资500亿元!中芯京城正式注册成立!
- Shell script foundation - shell operation principle + variable and array definitions
- [database]
- Xinhuazhang announced the completion of more than 200million a-round financing and the comprehensive layout of eda2.0 R & D
- TypeError: can‘t convert cuda:0 device type tensor to numpy. Use Tensor. cpu() to copy the tensor to
- CGLIb 创建代理
- 18张图,直观理解神经网络、流形和拓扑
- Cglib create proxy
- Mspba [anomaly detection: representation_based]
猜你喜欢

Servlet的使用手把手教学(一)

Sqlilabs-1 (breakthrough record)
![[3D target detection] 3dssd (II)](/img/8a/e8927cd868eb99d8880d4f199d8918.png)
[3D target detection] 3dssd (II)

Yolov5 improvement 7: loss function improvement
![ValueError: Using a target size (torch.Size([64])) that is different to the input size (torch.Size([](/img/c2/01dcdef191e7c932fef8f34a1c7f0a.png)
ValueError: Using a target size (torch.Size([64])) that is different to the input size (torch.Size([

Thesis reading (3) - googlenet of classification

NPM run dev, automatically open the browser after running the project

sql优化常用的几种方法

Target detection notes - overview and common data sets

PCA学习
随机推荐
赋能中国芯创业者!看摩尔精英如何破解中小芯片企业发展难题
Is 1E3 a floating point number?
WebApplicationType#deduceFromClasspath
2020年国内十大IC设计企业曝光!这五大产业挑战仍有待突破!
DirectX修复工具下载(exagear模拟器数据包在哪里)
Yolov5 improvement 4: add ECA channel attention mechanism
Learning experience sharing 4: learning experience of yolov7
Symbol符号类型
[radar] radar signal online sorting based on kernel clustering with matlab code
Learning experience sharing 3: yolov5 training data set path index
轮子七:TCP客户端
Reading of "robust and communication efficient federated learning from non-i.i.d. data"
《Robust and Communication-Efficient Federated Learning From Non-i.i.d. Data》论文阅读
cnpm安装步骤
【雷达】基于核聚类实现雷达信号在线分选附matlab代码
Sqlilabs-2 (breakthrough record)
It's settled! All products of Nezha s will be launched on July 31
Will Qualcomm and MediaTek chips soon be sold, and will they surpass Huawei to become the first in China?
Invest 50billion yuan! SMIC capital was officially registered!
ValueError: Using a target size (torch.Size([64])) that is different to the input size (torch.Size([