【C语言】深入讲解指针(中)

news2024/11/14 18:14:38

文章目录

  • 前言
  • 函数指针
    • 函数指针变量的创建
    • 函数指针变量的使用
    • 两段有趣的代码
      • typedef 关键字
  • 函数指针数组
  • 函数指针的使用
  • 最后

前言

上一章深入讲解指针(上)我们对字符指针、数组指针、指针和数组传参进行了讲解,本章将对函数指针进行讲解,本章内容可能比较难,希望对各位初学者在学习指针时有帮助。

函数指针

函数指针变量的创建

什么是函数指针变量呢?

根据前⾯学习整型指针,数组指针的时候,我们的类⽐关系,我们不难得出结论:
函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。
那么函数是否有地址呢?
首先看一段代码:

void test()
{
 	printf("hehe\n");
}
int main()
{
 	printf("%p\n", test);
 	printf("%p\n", &test);
 	return 0;
}

结果如下
在这里插入图片描述
确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过 &函数名 的⽅
式获得函数的地址。
如果我们要将函数的地址存放起来,就得创建函数指针变量咯,函数指针变量的写法其实和数组指针
⾮常类似。如下:

void test()
{
   printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)()= test;
int Add(int x, int y)
{
	return x+y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add

函数指针类型解析:

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

总结:
不同于数组名,函数名和&函数名是一个意思,所以**pf3、*pf3、Add、&Add**是一个意思。

函数指针变量的使用

通过函数指针调⽤指针指向的函数。

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int(*pf3)(int, int) = Add;
	printf("%d\n", (*pf3)(3, 3));
	printf("%d\n", pf3(3, 6));
	return 0;
}

输出结果:
在这里插入图片描述

两段有趣的代码

出⾃:《C陷阱和缺陷》这本书
代码1

(*(void (*)())0)();

分析如下

从0开始分析,先跟(void (*) () )结合被强制类型转化为一个的函数指针(这个函数无参返回类型为void),再解引用0,找到那个函数指针指向的函数,调用哪个函数。
代码1其实是函数的调用。
代码2

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

从signal开始分析,signal是一个函数,函数有了函数名,有了参数。就差返回类型了,所以剩下的void (*)(int)是返回类型
代码2是signal的函数声明。

typedef 关键字

typedef 是⽤来类型重命名的,可以将复杂的类型,简单化。
⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤:

typedef unsigned int uint;

如果是指针类型,能否重命名呢?其实也是可以的,⽐如,将 int* 重命名为 ptr_t ,这样写:

typedef int* ptr_t;

但是对于数组指针和函数指针稍微有点区别:
⽐如我们有数组指针类型 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 *arr[10];

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

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

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

函数指针的使用

函数指针数组的⽤途:转移表
举例:计算器的⼀般实现:

#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.coloradmin.cn/o/2048468.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java、python、php版的大学生家教预约服务系统的设计与实现(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

滚珠丝杆磨损评估:何时需进行研磨修复的关键指标?

滚珠丝杆是一种常见的机械传动元件&#xff0c;是将回转运动转化为直线运动&#xff0c;或将直线运动转化为回转运动的理想产品&#xff0c;在工具机械和精密机械上最常使用的传动元件。由于长时间使用或使用方法不当&#xff0c;滚珠丝杆可能会出现磨损、折裂、腐蚀等问题。影…

开机启动项检查

目录 介绍步骤总结 介绍 Windows在启动的时候&#xff0c;会自动加载很多程序。这些程序的自启动&#xff0c;带来了便利&#xff0c;但如果是恶意的自启动程序&#xff0c;我们就要关闭掉。 步骤 1、在开机后&#xff0c;打开了一个flag文件&#xff0c;里面填写了一个flag…

【解析几何笔记】4.向量分解定理的应用

4.向量分解定理的应用 4.1 简单比&#xff08;定比&#xff09; 【例1.3】&#xff08;由 ( A , B , D ) , ( C , A , F ) (A,B,D),(C,A,F) (A,B,D),(C,A,F)可以看出用 A C ⃗ , A B ⃗ \vec{AC},\vec{AB} AC ,AB 和从A点做起点的向量方便解题&#xff0c;再看题目所求的简单比…

Spring 事务配置类,完成数据库的转账

1、完成基本的三层架构 1.1创建Account表 创建实体类 Account 1.2 Service层写入 AccountService 接口 Service层 下写 impl 包定义 AccountServiceImpl 类 实现接口 AccountService Service Transactional RequiredArgsConstructor public class AccountServiceImpl impleme…

【网络】高并发场景处理:线程池和IO多路复用

文章目录 短时间内有大量的客户端的解决方案线程池IO 多路复用 短时间内有大量的客户端的解决方案 创建线程是比较经典的一种服务器开发模型&#xff0c;给每个客户端分配一个线程来提供服务 但一旦短时间内有大量的客户端&#xff0c;并且每个客户端请求都是很快的&#xff…

电路笔记(PCB):串扰的原理与减少串扰的几种方法

串扰 串扰&#xff08;Crosstalk&#xff09;是指在电路中&#xff0c;一条信号线上的电磁干扰不经意间耦合到另一条相邻的信号线上&#xff0c;从而影响其正常信号传输的现象。串扰会导致相邻信号线上的信号出现畸变或噪声&#xff0c;从而影响信号的完整性和电路的正常工作。…

【ARM+Codesys 客户案例 】 基于RK3568/A40i/STM32+CODESYS在智能制造中的应用案例:液压动力装置

Poppe Potthoff是一家专门从事高压领域技术研发和产品制造的集团公司&#xff0c;该公司为汽车行业、特种车辆行业、船舶行业等开发制造先进的技术产品。 信迈提供ARMCodesys国产化定制。 Poppe Potthoff在其诺德豪森工厂研发用于爆破测试&#xff0c;自应力加工、脉冲测试和…

深度学习-------------------使用块的网络VGG

目录 VGGVGG块VGG架构进度总结 使用块的网络&#xff08;VGG&#xff09;VGG块VGG网络观察每层输出的形状该部分总代码 训练模型 VGG AlexNet比LeNet更深更大&#xff0c;以得到更好的精度 能不能更深和更大&#xff1f;该如何更深更大&#xff1f; 选项&#xff1a; 更多的…

阿里Qwen2开源大模型本地部署及调试全攻略

阿里Qwen2开源大模型本地部署及调试全攻略 #Qwen2系列大模型性能卓越&#xff0c;超越业界知名模型。开源后受到AI开发者关注&#xff0c;支持多种语言&#xff0c;提升多语言理解。在预训练和微调上优化&#xff0c;实现智能水平提升。Qwen2系列模型在各项能力上均领先&#…

基于NXP i.MX8M Mini+FPGA医疗超声诊断设备

医疗超声诊断设备 超声诊断仪&#xff0c;又称超声医疗影像设备&#xff0c;是利用超声波的反射、折射和衍射&#xff0c;对人体内部结构进行探查的仪器。由于超声诊断的无损伤、非介入、经济实用、应用范围广等特点&#xff0c;超声诊断仪也成为目前医疗领域普遍使用的医疗器…

C++_进阶:二叉搜索树

文章目录 1. 二叉搜索树是什么2. 二叉搜索树的基本操作3. 二叉搜索树的实现4 二叉搜索树的性能分析 1. 二叉搜索树是什么 二叉搜索树(BST&#xff0c;Binary Search Tree)又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不…

汉诺塔问题详解及扩展(c++)

汉诺塔&#xff08;Hanoi Tower&#xff09;问题是一个著名的数学问题&#xff0c;它涉及到递归算法。问题的背景来源于一个传说&#xff1a;在印度的一个寺庙里&#xff0c;有三根金刚石柱和64个直径大小不一的金盘。僧侣们被命令将这些金盘从一根柱子按照从小到大的顺序移动到…

北京移民服务机构亨瑞移民:汇集全球专业顾问 30年致力全球生活方式

01      北京移民服务机构亨瑞集团成立于1992年&#xff0c;作为一站式海外综合服务专业平台&#xff0c;30年来致力于为有意赴海外求学、拓展事业、居住、生活、工作的各行业人士提供全球教育规划、身份规划及资产全球配置解决方案。    北京移民服务机构亨瑞集团公司…

在 C#/.NET Core 的 Web API 中使用 Swagger 按模块和版本分组并实现排序

文章目录 前言步骤一&#xff1a;安装 Swashbuckle.AspNetCore步骤二&#xff1a;创建自定义特性步骤三&#xff1a;配置 Swagger 生成文档步骤四&#xff1a;标记控制器和方法总结 前言 在开发 RESTful API 时&#xff0c;良好的文档是必不可少的。Swagger 是一种广泛使用的 …

C/C++ 多线程[1]---线程创建+线程释放+实例

文章目录 前言1. 多线程创建2. 多线程释放3. 实例总结 前言 说来惭愧&#xff0c;写了很久的代码&#xff0c;一个单线程通全部。可能是接触的项目少吧&#xff0c;很多多线程的概念其实都知道&#xff0c;但是实战并没有用上。前段时间给公司软件做一个进度条&#xff0c;涉及…

【Docker系列】Docker 容器时区设置指南

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

中国式现代化产业水平数据集(2011-2021年)

中国式现代化是一种社会主义现代化模式&#xff0c;它具有自己独特的特征和发展路径。这一现代化模式以实现国家富强、民族振兴和人民幸福为目标&#xff0c;强调物质文明与精神文明的协调发展以及人与自然的和谐共生 本文将中国式现代化理念与现代化产业体系相结合&#xff0…

希亦、洁盟、苏泊尔眼镜清洗机哪款好用?热门眼镜清洗机测评总结

随着科学技术的发展&#xff0c;电子设备的升级&#xff0c;越来越多的人开始戴眼镜&#xff0c;而眼镜由于长时间的佩戴&#xff0c;镜框以及镜面都积累了一些灰尘以及人们肉眼所看不见的细菌&#xff0c;但是如果你使用普通的清洁方式去清洗的话肯定是清洗不干净的&#xff0…

记录|Label组件如何控制下边框为直线

目录 前言一、问题描述二、重绘三、效果展示更新时间 前言 参考文章&#xff1a; C# WinForm开发时&#xff0c;仅显示label的下边框怎么解决啊&#xff1f; 验证过&#xff0c;方法可靠。并增加控制绘制的直线粗细的功能。 一、问题描述 C# winform中想只给Label组件的下边框…