函数指针与指针函数的区别
目录:
一、函数指针
1 函数类型
2 函数指针(指向函数的指针)
3 函数指针数组
二.函数指针和指针函数比较
1 定义不同
2 写法不同
3.用法不同
三.函数指针做函数参数(回调函数)
1 利用回调函数实现打印任意类型数据
2 提供能够打印任意类型数组函数
3 利用回调函数 提供查找功能
四.练习
一、函数指针
1 函数类型
通过什么来区分两个不同的函数?
一个函数在编译时被分配一个入口地址,这个地址就称为函数的指针,函数名代表函数的入口地址。
函数三要素: 名称、参数、返回值。C语言中的函数有自己特定的类型。
这一点和数组一样,因此我们可以用一个指针变量来存放这个入口地址,然后通过该指针变量调用函数。
注意:通过函数类型定义的变量是不能够直接执行,因为没有函数体。只能通过类型定义一个函数指针指向某一个具体函数,才能调用。举例如下:
typedef int(p)(int, int);void my_func(int a,int b){printf("%d %d\n",a,b);
}void test(){p p1;//p1(10,20); //错误,不能直接调用,只描述了函数类型,但是并没有定义函数体,没有函数体无法调用p* p2 = my_func;p2(10,20); //正确,指向有函数体的函数入口地址
}
2 函数指针(指向函数的指针)
函数指针定义方式(先定义函数类型,根据类型定义指针变量); typedef void(FUNC_TYPE)(int, char);
先定义函数指针类型,根据类型定义指针变量; typedef void( * FUNC_TYPE2)(int, char);
直接定义函数指针变量; void(*pFunc3)(int, char) = func;
void func(int a, char c)
{printf("hello world\n");
}void test01()
{//1、先定义出函数类型,再通过类型定义函数指针typedef void(FUNC_TYPE)(int, char);FUNC_TYPE* pFunc = func;//pFunc(10, 'a');//2、定义出函数指针类型,通过类型定义函数指针变量typedef void(*FUNC_TYPE2)(int, char);FUNC_TYPE2 pFunc2 = func;//pFunc2(20, 'b');//3、直接定义函数指针变量void(*pFunc3)(int, char) = func;pFunc3(30, 'c');
}
个人理解:和多维数组 中提到的数组指针非常类似,定义也是三种形式。这里func是一个地址名称,因此需要用一个地址名称去对等赋值。
第一个方式,定义普通类型,需要将pFunc转换成地址形态去对等,才有后面的写法pFunc(10, 'a');
第二方式,因为类型已经是指针类型,定义出来的本来就是地址名称(即指针变量),因此直接对等;
第三种方式,和第二种相同,直接弄一个指针,省略了再赋值地址名称。
3 函数指针数组
//函数指针的数组
void func1()
{printf("func1 调用了\n");
}void func2()
{printf("func2 调用了\n");
}void func3()
{printf("func3 调用了\n");
}void test02()
{void(*pArray[3])();pArray[0] = func1;pArray[1] = func2;pArray[2] = func3;for (int i = 0; i < 3; i++){pArray[i]();}
}
二.函数指针和指针函数比较
1 定义不同
指针函数本质是一个函数,其返回值为指针。
函数指针本质是一个指针,其指向一个函数。
2 写法不同
指针函数:int* fun(int x,int y);
函数指针:int (*fun)(int x,int y);
可以简单粗暴的理解为,指针函数的*是属于数据类型的,而函数指针的星号是属于函数名的。
一个很简单的判定方法就是观察(*),记住其中一个即可:
int (*p)(int,int);有括号,*与p结合,*p就是个指针,指向返回值为整型且有两个整型参数的函数的指针,因此称为函数指针。
int*p(int,int); 没有括号,*与int结合,int*为返回类型,p就是一个函数名,这时就是一个指针函数,只不过返回值类型为int*。
3 用法区别
1)指针函数
指针函数的写法
int *fun(int x,int y);
int * fun(int x,int y);
int* fun(int x,int y); //建议这种
这个写法看个人习惯,其实如果*靠近返回值类型的话可能更容易理解其定义。
注意:在调用指针函数时,需要一个同类型的指针来接收其函数的返回值。
也可以将其返回值定义为 void*类型,在调用的时候强制转换返回值为自己想要的类型,不过不建议这么使用,因为强制转换可能会带来风险。
typedef struct _Data {int a;int b;
}Data;//指针函数Data* f(int a, int b) {Data data = {a, b};return &data;
}int test02()
{Data* myData = f(3, 5);printf("函数指针myData = %d, b = %d\n",myData->a, myData->b);
}
2)函数指针
函数指针是需要把一个函数的地址赋值给它,有两种写法:
fun=&Function;
fun=Function;
取&不是必需的,因为一个函数标识符就表示了它的地址,如果是函数调用,还必须包含一个圆括号括起来的参数表。
调用函数指针的方式也有两种:
x=(*fun)();
x=fun();
两种方式均可,其中第二种看上去和普通的函数调用没啥区别,如果可以的话,建议使用第一种,因为可以清楚的指明这是通过指针的方式来调用函数。当然,也要看个人习惯,如果理解其定义,随便怎么用都行啦。
int add(int x, int y) {return x + y;
}
int sub(int x, int y) {return x - y;
}
//函数指针
int test02()
{int (*fun)(int x, int y);//第一种写法fun = add;printf("(*fun)(5, 3) = %d\n", (*fun)(5, 3));//第二种写法fun = ⊂printf("(*fun)(5, 3) = %d,(fun)(5, 3) = %d\n",(*fun)(5, 3), fun(5, 3));}
三.函数指针做函数参数(回调函数)
函数参数除了是普通变量,还可以是函数指针变量。
函数指针变量常见的用途之一是把指针作为参数传递到其他函数,指向函数的指针也可以作为参数,以实现函数地址的传递。
//形参为普通变量
void fun( int x ){}
//形参为函数指针变量
void fun( int(*p)(int a) ){}
1 利用回调函数实现打印任意类型数据
//提供一个打印函数,可以打印任意类型的数据
void printText(void* data, void(*myPrint)(void*))
{myPrint(data);
}void myPrintInt(void* data)
{int* num = data;printf("%d\n", *num);
}void test01()
{int a = 10;printText(&a, myPrintInt);
}
void printText(void* data, void(*myPrint)(void*))
{myPrint(data);
}struct Person
{char name[64];int age;
};void myPrintPerson(void * data)
{struct Person * p = data;printf("姓名: %s 年龄: %d\n", p->name, p->age);
}void test02()
{struct Person p = { "Tom", 18 };printText(&p, myPrintPerson);
}
2 提供能够打印任意类型数组函数
//提供一个函数,实现可以打印任意类型的数组
void printAllArray(void * pArray , int eleSize, int len , void(*myPrint)(void*) )
{char * p = pArray;for (int i = 0; i < len;i++){//获取数组中每个元素的首地址char * eleAddr = p + eleSize * i;//printf("%d\n", *(int *)eleAddr);//交还给用户做打印操作myPrint(eleAddr);}
}void myPrintInt(void * data)
{int * num = data;printf("%d\n", *num);
}void test01()
{int arr[5] = { 1, 2, 3, 4, 5 };int len = sizeof(arr) / sizeof(int);printAllArray(arr, sizeof(int), len, myPrintInt);
}
3 利用回调函数 提供查找功能
struct Person
{char name[64];int age;
};void myPrintperson(void * data)
{struct Person * p = data;printf("姓名:%s 年龄:%d \n", p->name, p->age);
}//查找数组中的元素是否存在
//参数1 数组首地址 参数2 每个元素的大小 参数3 数组元素个数 参数4 查找数据
int findArrayEle(void * pArray, int eleSize, int len, void * data , int(*myCompare)(void* ,void* ) )
{char * p = pArray;for (int i = 0; i < len;i++){//每个元素的首地址char * eleAddr = p + eleSize * i;//if ( 数组中的变量的元素 == 用户传入的元素)if ( myCompare(eleAddr,data) ){return 1;}}return 0;
}int myComparePerson(void * data1,void * data2)
{struct Person * p1 = data1;struct Person * p2 = data2;return strcmp(p1->name, p2->name) == 0 && p1->age == p2->age;
}void test02()
{struct Person personArray[] ={{ "aaa", 10 },{ "bbb", 20 },{ "ccc", 30 },{ "ddd", 40 },};int len = sizeof(personArray) / sizeof(struct Person);printAllArray(personArray, sizeof(struct Person), len, myPrintperson);//查找数组中指定的元素是否存在struct Person p = { "ccc", 30 };int ret = findArrayEle(personArray, sizeof(struct Person), len, &p, myComparePerson);if (ret){printf("找到了元素\n");}else{printf("未找到\n");}
}
四.练习
提供一个函数,实现对任意类型的数组进行排序,排序规则利用选择排序,排序的顺序 用户可以自己指定
void selectSort(void* pAddr, int elesize, int len, int(*myCompare)(void*, void*))
{char* temp = malloc(elesize);for (int i = 0; i < len; i++){int minOrMax = i; //定义最小值 或者 最大值 下标for (int j = i + 1; j < len; j++){//定义出 j元素地址char* pJ = (char*)pAddr + elesize * j;char* pMinOrMax = (char*)pAddr + elesize * minOrMax;//if ( pAddr[j] < pAddr[minOrMax])/* 从大到小if ( *num1 > *num2){return 1;}return 0;*/if (myCompare(pJ, pMinOrMax)){minOrMax = j; //更新最小值或者最大值下标}}if (i != minOrMax){//交换i和minOrMax 下标元素char* pI = (char*)pAddr + i * elesize;char* pMinOrMax = (char*)pAddr + minOrMax * elesize;memcpy(temp, pI, elesize);memcpy(pI, pMinOrMax, elesize);memcpy(pMinOrMax, temp, elesize);}}if (temp != NULL){free(temp);temp = NULL;}}int myCompareInt(void* data1, void* data2)
{int* num1 = data1;int* num2 = data2;if (*num1 > *num2){return 1;}return 0;
}void test01()
{int arr[] = { 10, 30, 20, 60, 50, 40 };int len = sizeof(arr) / sizeof(int);selectSort(arr, sizeof(int), len, myCompareInt);for (int i = 0; i < len; i++){printf("%d\n", arr[i]);}
}struct Person
{char name[64];int age;
};int myComparePerson(void* data1, void* data2)
{struct Person* p1 = data1;struct Person* p2 = data2;//if ( p1->age < p2->age)//{// return 1;//}//return 0;//按照年龄 升序排序return p1->age < p2->age;
}void test02()
{struct Person pArray[] ={{ "aaa", 10 },{ "bbb", 40 },{ "ccc", 20 },{ "ddd", 30 },};int len = sizeof(pArray) / sizeof(struct Person);selectSort(pArray, sizeof(struct Person), len, myComparePerson);for (int i = 0; i < len; i++){printf("姓名:%s 年龄:%d\n", pArray[i].name, pArray[i].age);}
}
相关文章:
函数指针与指针函数的区别
目录:一、函数指针1 函数类型2 函数指针(指向函数的指针)3 函数指针数组二.函数指针和指针函数比较1 定义不同2 写法不同3.用法不同三.函数指针做函数参数(回调函数)1 利用回调函数实现打印任意类型数据2 提供能够打印任意类型数组函数3 利用回调函数 提供查找功能四…...
死锁的四个必要条件以及如何避免死锁
死锁的四个必要条件以及如何避免死锁 一.什么是死锁?二.死锁的四个必要条件 1.互斥条件:2.请求与保持条件:3.不剥夺条件:4.循环等待条件: 三.如何避免死锁 1.破坏请求保持条件2.破坏不剥夺条件3.破坏循环等待条件 死锁的四个必要条件以及如…...
浏览器多线程到事件循环机制
浏览器与js运行机制 进程与线程 进程 进程是CPU分配资源的最小单位,它是一个可以自己独立运行且拥有自己资源空间的任务程序;包括程序以及程序所使用的内存及系统资源 线程 线程是CPU调度的最小单位,它就是程序中的一个执行流࿱…...
Lambda表达式的本质
一直想写一篇文章,来总结lambda表达式,但是之前感觉总结的不是特别到位,现在看了几篇文章和视频后,感觉对lambda表达式有了比较深刻的认识,现在进行记录总结如下: lambda表达式又叫做匿名函数,…...
类的加载过程(生命周期)
类的加载过程(生命周期) 一、装载:通过一个类的全限定名获取定义此类的二进制字节流将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构在内存中生成一个代表这个类的java.lang.Class对象(将字节码加载到内存中),作为…...
2023最新谷粒商城笔记之MQ消息队列篇(全文总共13万字,超详细)
MQ消息队列 其实队列JDK中本身就有,不过这种队列也只能单体服务可能会使用,一旦项目使用的分布式架构,那么一定还是需要用到一个消息中间件的。我们引入消息队列的原因就是对我们的页面相应速度再优化,让用户的体验更好ÿ…...
多变量线性回归模型
多变量线性回归模型 模型参数为n1维向量,此时模型公式为 hθ(x)θ0x0θ1x1θ2x2...θnxnh_{\theta}(x)\theta_{0}x_{0}\theta_{1}x_{1}\theta_{2}x_{2}...\theta_{n}x_{n} hθ(x)θ0x0θ1x1θ2x2...θnxn 可以简化为 hθ(x)θTXh_{\theta}(x)\th…...
php 基于ICMP协议实现一个ping命令
php 基于ICMP协议实现一个ping命令 网络协议是什么ICMP 协议什么是ICMP?ICMP 的主要功能ICMP 在 IPv4 和 IPv6 的封装Wireshark抓包ICMP 请求包分析PHP构建 ICMP 数据包php中的 pack & unpack 函数字节和字符packunpackICMP计算校验和步骤总结网络协议是什么 网络协议&…...
Java基本数据类型
1.概述 佛说,大千世界,无奇不有。在这个世界里,物种的多样性,遍地开花,同样,在Java的世界里,也有着异曲同工之妙,Java秉承面向对象的特性,必然少不了区分对象的类型&…...
English Learning - L2 语音作业打卡 Day2 2023.2.22 周三
English Learning - L2 语音作业打卡 Day2 2023.2.22 周三💌 发音小贴士:💌 当日目标音发音规则/技巧:🍭 Part 1【热身练习】🍭 Part2【练习内容】🍭【练习感受】🍓元音[ ɑː ]&…...
45. 跳跃游戏 II
题目: 45. 跳跃游戏 II难度中等1974收藏分享切换为英文接收动态反馈给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 num…...
应届生Java面试50题线程篇(含解析)
什么是线程? 答:线程是操作系统能够进行运算调度的最小单位,是程序执行流的最小单元。在Java中,可以通过实现Runnable接口或继承Thread类来创建线程。 创建线程的方式有哪些?各自的优缺点是什么? 继承 Thread 类&…...
【数据库】第七章 数据库设计
第七章数据库设计 数据库设计概述 数据库设计的基本步骤 需求分析概念结构设计逻辑结构设计物理结构设计数据库实施数据库运行和维护 需求分析 收集需求,理解需求 收集各个角色的需求 概念数据库设计 建立概念模型 ,E-R图/IDEF1x图 消除冲突&…...
Burp Suite 常用模块简介
Burp Suite 常用模块分为 目标站点(target)模块 代理(proxy)模块 攻击(Intruder)模块 重放(Repeater) 模块 Target模块是对站点资源的收集,与站点各资源包发出和相应包的记录 Proxy模块是核心模块,可以拦截数据包发送往浏览器,进行修改后再…...
QML Item和Rectangle详解
1.Item和Rectangle Item类型是Qt Quick中所有可视项的基本类型。 Qt Quick中的所有可视项都继承Item。尽管Item对象没有视觉外观,但它定义了视觉项中常见的所有属性,例如x和y位置、宽度和高度、锚定和键处理支持。 Rectangle继承自Item,多…...
常见前端基础面试题(HTML,CSS,JS)(六)
GET 和 POST 的区别 从 http 协议的角度来说,GET 和 POST 它们都只是请求行中的第一个单词,除了语义不同,其实没有本质的区别。 之所以在实际开发中会产生各种区别,主要是因为浏览器的默认行为造成的。 受浏览器的影响…...
深度学习 李沐报错
3.6. softmax回归的从零开始实现 — 动手学深度学习 2.0.0 documentation softmax从0开始实现 函数执行需要加main指定 改成这样 if __name__"__main__":print(evaluate_accuracy(net, test_iter)) 不然会这样出错 RuntimeError: An attempt has been m…...
【JAVA程序设计】(C00104)基于Springboot的家庭理财管理系统——有文档
基于Springboot的家庭理财管理系统项目简介项目获取开发环境项目技术运行截图运行视频项目简介 基于Springboot开发的家庭理财管理系统设计与实现共分为三个角色:系统管理员、家庭管理员、家庭用户 管理员角色包含以下功能: 用户管理、修改密码、角色管…...
【第五章 AOP概述,底层原理,AOP术语,切入点表达式,AOP操作(基于注解方式,基于xml配置文件)】
第五章 AOP概述,底层原理,AOP术语,切入点表达式,AOP操作(基于注解方式,基于xml配置文件) 1.AOP概述: (1)什么是AOP: ①面向切面编程(…...
面试官: 你知道 JWT、JWE、JWS 、JWK嘛?
想起了 之前做过的 很多 登录授权 的项目 它相比原先的session、cookie来说,更快更安全,跨域也不再是问题,更关键的是更加优雅 ,所以今天总结了一篇文章来介绍他 JWT 指JSON Web Token,如果在项目中通过 jjwt 来支持 J…...
基于企业微信应用消息的每日早安推送
基于企业微信应用消息的每日早安推送 第一步:注册企业微信 企业微信注册地址:https://work.weixin.qq.com/wework_admin/register_wx 按照正常流程填写信息即可,个人也可以注册企业微信,不需要公司 注册完成后,登录…...
【数字IC基础】黑盒验证、白盒验证、 灰盒验证
文章目录 一、黑盒验证二、白盒验证三、灰盒验证一、黑盒验证 1、黑盒验证:大多数基于仿真的验证环境都是黑盒验证;2、不需要知道设计的内部结构和特性,只需要在输入端口打激励,观察输出即可;3、验证工程师学习设计的规格,然后编写验证环境中的 drivers, monitors, check…...
管理的本质是达成目标
“没有目标,其实就没有管理学存在的意义。要有效地使用管理学的智慧,首先要建立清晰的目标。” - 《宁向东的管理学课》 起源 最近开始刷很久之前就在得到上买了的已经起灰了的课程,看到这句话觉得很有道理。 思考 这里面有一个很重要的词…...
【数字IC基础】IC(Integrated Circuit,集成电路)常用缩写
文章目录 1、集成电路:2、数字IC设计相关步骤:3、数字设计相关概念:4、验证相关:5、语言类:6、IC设计相关工具:7、存储器相关:8、总线协议类:9、文件格式类:10、标准和规范:11、其它:1、集成电路: 缩写全称中文翻译LSILarge-scale intergrated circuit大规模集成电…...
JavaScript 高级1 :面向对象
JavaScript 高级1 :面向对象 Date: January 16, 2023 Text: 面向对象、ES6中类和对象、类的继承、面向对象案例 目标: 能够说出什么是面向对象 能够说出类和对象的关系 能够使用 class 创建自定义类型 能够说出什么是继承 面向对象编程介绍 面向过…...
C语言结构体对齐
1. 结构体对齐 要点 变量只能存储在他的长度的整数倍地址上结构体整体对齐跟他的最长的字段整数倍对齐 栗子1 struct Example1 {char a; //1个字节int c; //4个字节short b; //2个字节 };std::cout << sizeof(Example1 ) << std::endl; // 12 std::cout &…...
Bootstrap系列之导航
Bootstrap导航 可以在 ul 元素上添加 .nav类,在每个 li 选项上添加 .nav-item 类,在每个链接上添加 .nav-link 类: 基本的导航 <div class"container mt-3"><h2>导航</h2><p>简单的水平导航:</p><ul class&…...
Java EE|TCP/IP协议栈之应用层协议DNS详解
文章目录一、对DNS的感性认识简介特点一些常见疑问二、DNSDNS域名结构域名的分级三、域名服务器四、域名解析过程参考一、对DNS的感性认识 简介 DNS,即Domain Name System,是域名系统的简称。它是Internet上解决网上机器命名的一种系统。 TCP/IP中的IP地址是由四…...
【MyBatis】作用域生命周期(四)
🚗MyBatis学习第四站~ 🚩起始站:MyBatis概述&环境搭建(一) 🚩本文已收录至专栏:数据库学习之旅 👍希望您能有所收获 一.引入 为了使用方便,我们经常能看到各种教程都将MyBatis抽离为工具类…...
腾讯一面—Android 系统启动流程详解
正文AMS 是 Android 中最核心的服务之一,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似,它本身也是一个 Binder 的实现类,应用进程能通过 Binder 机制调用…...
bs系统做的网站有什么特点/爱链接
2019独角兽企业重金招聘Python工程师标准>>> 近期的一个项目中用到将oracle blob转换为base64的字符串 //转换成base64编码(不换行)需要commons-codec1.5jar //换行使用encoder.encode(user.getZp()); Action中 String cardBase64encoder.enc…...
做个商城网站要多少钱/杭州推广系统
问题截图: 添加#include<sys/mman.h>头文件可解决’PROT_WRITE’、‘MAP_SHARED’、未定义的问题。 现在还剩O_RDRW未定义的问题。 我再想想吧 先(~ ~) 这问题出现的原因是对应库文件的缺失,添加相应的库文件即可,但是我查了查资料后…...
wordpress滑动门/青岛app开发公司
鉴于numpy中矩阵的运算居多,所以以下内容中参与运算的数据元素都默认为矩阵,且以矩阵的逐元素相乘这个操作为例子进行说明。 numpy中的广播(broadcast)说白了就是干一件事——扩展矩阵,而且扩展对象都是纬度比较小的矩阵,将维度较…...
做网站表格/seo优化关键词排名优化
文章目录项目整体结构依赖openfeign的一些配置order-service-apiorder-servicepay-service测试源码分析源码下载项目整体结构 说明: 所有公共依赖都放在了父pom中,API接口抽离放在单独模块 依赖 <properties><java.version>1.8</java.…...
一个公司完整的组织架构/宁波seo网络推广
#python中的堆排序peapq模块 heapq模块实现了python中的堆排序,并提供了有关方法。让用Python实现排序算法有了简单快捷的方式。 heapq的官方文档和源码:8.4.heapq-Heap queue algorithm 下面通过举例的方式说明heapq的应用方法 ##实现堆排序 Python#! /…...
新郑做网站推广/打开百度一下的网址
本文讲三种content-type以及在Jmeter中对应的参数输入方式 第一部分:目前工作中涉及到的content-type 有三种: content-type:在Request Headers里,告诉服务器我们发送的请求信息是哪种格式的。 1 content-type:application/x-www-…...