KEIL中 C语言的结构体定义函数指针中能 声明函数指针么

周立功教你学C语言编程:结构体,使程序设计更方便——内置函数指针和嵌套结构体
第二章为程序设计技术,本文为2.2.3 内置函数指针和2.2.4 嵌套结构体。
我们知道,数组和指针是相同类型有序数据的集合,但很多时候需要将不同类型的数据捆绑在一起作为一个整体来对待,使程序设计更方便。在C语言中,这样的一组数据被称为结构体。
>>>&2.2.3&内置函数指针
面对一系列数据,真正重要的不是如何存储数据,而是如何使用数据。实际上,一个结构体的成员可以是数据,还可以是包含操作数据的函数指针。为了支持这种风格,在这里不妨引入一个新的概念&&方法是作为某个结构体的一部分声明的,有了方法就可以操作存储在结构体中的数据。
1.&类型与变量
当函数指针作为结构体的成员时,即将校验参数和调用校验器的函数指针封装在一起,形成了一个新的结构体类型。有了类型就可以定义一个该类型的变量,然后就可以用这个变量引用校验参数和调用校验器函数。
为了支持这种风格,C允许将方法作为某个结构体的一部分来声明,那么操作存储在结构体中的数据就很容易了,详见程序清单&2.18。
程序清单 2.18 范围值校验器接口
接下来需要设计一个判断value值是否符合范围值要求的validateRange()接口函数,其具体的实现详见程序清单&2.19。
程序清单 2.19 范围值校验器接口函数的实现
同理,偶校验器OddEvenValidator和变量oddEvenValidator的定义详见程序清单&2.20。
程序清单 2.20 偶校验器接口
接下来同样需要设计一个判断value值是否符合偶校验要求的validateOddEven()接口函数,其具体的实现详见程序清单&2.21。
程序清单 2.21 偶校验器接口函数的实现
显然,无论是什么校验器,其共性是value值合法性判断,因此可以共用一个函数指针,即特殊的函数指针类型RangeValidate和OddEvenValidate被泛化成了一般的函数指针类型Validate。其次,由于每个函数都有一个指向当前对象的pThis指针,因此特殊的结构体类型struct _RangeValidator&*和struct _OddEvenValidator *被泛化成了void *类型,即可接受任何类型数据的实参。比如:
这就是范型编程,校验器泛化接口的实现详见程序清单 2.22。由于pRangeValidator与pThis的类型不同,因此必须对pThis指针强制类型转换才能引用相应结构体的成员。
程序清单 2.22 通用校验器接口的实现(validator.c)
由此可见,当将方法作为结构体的一部分声明时,就直接将方法和数据打包成为了一个新的数据类型RangeValidator。有了RangeValidator类型,就可以创建一个该类型的变量rangeValidator,即可通过rangeValidator引用该结构体的数据,并调用相应的处理函数。真正想强化的是由方法定义结构体的思想,而不是实现结构体时碰巧用到的那些数据。
使用名为newRangeValidator的宏将结构体初始化:
其中,validateRange为范围值校验器的函数名,使用方法如下:
宏展开后如下:
其相当于:
如果有以下定义:
即可通过pValidator引用RangeValidator的min和max。校验函数的调用方式如下:
以上调用形式的前提是已知pValidator指向了确定的结构体类型,如果pValidator将指向未知的校验器,显然以上调用形式无法做到通用,那么将如何调用?
虽然pValidator与&rangeValidator.validate的类型不一样,但它们的值相等,因此可以利用这一特性获取validateRange()函数的地址。比如:
其调用形式如下:
3.&接口与实现
为了便于阅读,如程序清单&2.23所示详细地展示了通用校验器的接口。
程序清单 2.23 通用校验器接口(validator.h)
以范围值校验器为例,调用validateRange()的rangeCheck()函数的实现如下:
rangeCheck()函数的调用形式如下:
由此可见,rangeCheck()函数的实现不依赖任何具体校验器。 注意,在这里,作者并没有提供完整的代码,请读者补充完善。
>>>&2.2.4&嵌套结构体
随着添加一个又一个功能,处理一个又一个错误,代码的结构会逐渐退化。如果对此置之不理,这种退化最终会导致纠结不清,难以维护的混乱代码,因此需要经常性地重构代码扭转这种退化。
重构就是在不改变代码行为的前提下,对其进行一系列小的改进,旨在改进系统结构的实践活动。虽然每个改进都是微不足道的,甚至几乎不值得去做,但如果将所有的改造叠加在一起时,对系统设计和架构的改进效果是十分明显的。
在每次细微改进后,通过运行单元测试以确保改进没有造成任何破坏,然后才去做下一次改进。如此往复周而复始,每次改进后都要运行,通过这种方式保证在改进系统设计的同时系统能够正常工作。
重构是持续进行的,而不是在项目结束时、发布版本时、迭代结束时、甚至每天下班时才进行。重构是每隔一个小时或半个小时就要去做的事情,通过重构可以持续地保持尽可能干净、简单且有表现力的代码。
大量的实践证明,重复可能是软件中一切邪恶的根源,许多原则和实践规则都是为了控制与消除重复而创建的。消除重复最好的方法就是抽象,即将所有公共的函数指针移到一个单独的结构体中,创建一个通用的Validator类型校验器。也就是说,如果两种事物相似的话,必定存在某种抽象能够统一它们,因此消除重复的行为会迫使团队提炼出许多的抽象,进一步减少代码之间的耦合。
自从发明子程序以来,软件开发领域的所有创新都是在不断尝试从源代码中消灭重复,即DRY(Don't Repeat Yourself)原则&&别重复自己,因为重复黏贴会带来很多的问题,所以无论在哪里发现重复的代码,都必须消除它们。
2.&&类型与变量
实际上,不管是范围值校验器还是奇偶校验器,其本质上都是校验器,其相同的属性是校验参数和待校验的值,其相同的行为可以共用一个函数指针调用不同的校验器。根据依赖倒置原则,将它们相同的属性和行为抽象为一个结构体类型Validator。比如:
在这里,还是以范围值校验为例,在RangeValidatro结构体中嵌套一个Validator类型的结构体,即将Validator类型的变量isa作为RangeValidator结构体的成员。比如:
由于&rangeValidator与&rangeValidator.isa的值相等,因此以下关系恒成立。比如:
即可将validateRange()函数原型:
中的&void *pThis&转换为&Validator *pThis&,validatrRange()函数原型进化为:
3.&&初始化
当将Validator类型的isa作为RangeValidator结构体成员时,显然rangeValidator.isa是一个结构体变量名,可以象任何普通结构体变量一样使用。使用Validator类型表达式:
即可引用rangeValidator变量的结构体成员isa的成员validate,即将rangeValidator.isa作为另一个点操作符的左操作符。比如:
由于点操作符的结合性是从左向右的,因此可以省略括号。其等价于:
只要将rangeValidator.isa看作一个Validator类型的变量即可。&
使用名为newRangeValidator的宏将结构体初始化:
其中,validateRange为范围值校验器函数名,使用方法如下:
宏展开后如下:
其中,外面的{}为RangeValidator结构体赋值,内部的{}为RangeValidator结构体的成员变量isa赋值。即:
如果有以下定义:
即可用pValidator引用RangeValidator的min和max。
由于pValidator与&rangeValidator.isa不仅类型相同且值相等,则以下关系同样成立:
因此可以利用这一特性获取validateRange()函数的地址,即pValidator->validate指向validateRange()。其调用形式如下:
4.&&接口与实现
以范围值校验器为例,validatorCheck()函数的调用形式如下:
当然,也可以采取以下调用形式:
其效果是一样的。
为了便于阅读,如程序清单 2.24所示详细地展示了通用校验器的接口。
程序清单 2.24通用校验器接口(validator.h)
以范围值校验器为例,调用validateRange()的validatorCheck()函数的实现如下:
由此可见,validatorCheck()函数的实现不依赖任何具体校验器,通用校验器接口的实现详见程序清单 2.25。
程序清单 2.25 &通用校验器接口的实现(validator.c)
在这里,作者并没有提供完整的代码,请读者补充完善。
原文标题:周立功:结构体,使程序设计更方便——内置函数指针和嵌套结构体
文章出处:【微信号:ZLG_zhiyuan,微信公众号:ZLG致远电子】欢迎添加关注!文章转载请注明出处。
关注电子发烧友微信
有趣有料的资讯及技术干货
下载发烧友APP
打造属于您的人脉电子圈
关注发烧友课堂
锁定最新课程活动及技术直播
发布评论请先
在产品设计时,倘若没有考虑应用环境对电源隔离的要求,产品到了应用时就会出现因设计方案的不当导致的系统....
空气净化器、净水器市场发展迅猛,很多品牌商都抢占到了自己的份额。但在实际使用过程中却发现很多用户长期....
M1052跨界核心板让设计者如开发普通MCU系统般简便,却拥有528MHz主频处理器的“不凡”性能。
为什么很多工程师创业失败?其根本原因不是资金,而是因为不了解市场,对市场缺乏一定前瞻性的认识,做出来....
号—4月3号,ZLG将携手NXP在全国6大城市(广州、深圳、上海、杭州、南京、北京....
目前市面上大多电力FTU产品均采用MCU+MPU双处理器架构,以利用MCU的实时性和MPU上运行的稳....
各位工程师在工业通讯现场,最担心的是通讯网络因浪涌产生的瞬态过压和过流,导致总线通讯网络出现发送错误....
RS-485总线具有结构简单、成本低等优点,但各位工程师在组建RS-485总线网络时,为提升整个网络....
C语言模拟打地鼠小游戏
虽然该程序的逻辑与程序清单6.70 所示的应用程序基本一致,但由于使用的接口是特殊功能控制接口,与具....
论选择I2C 或UART 通信方式,只要基于实例句柄编程,则应用程序与具体的通信方式无关。
当矩阵扩大到一定数目时,逐行扫描的方法会显得费时,如果需要对2 个以上的按键“同时”操作时,则处理起....
面向通用接口的编程,虽然面向接口的编程简单易懂,但无法做到最大程度上地重用应用程序,这是导致软件开发....
虽然各种排序的实现不一样,但它们的共性都是“排序”,这就是抽象的基础。
对AMetal框架进行了详细介绍,通过阅读这本书,你可以学到高度复用的软件设计原则和面向接口编程的开....
AMetal 提供了数码管和矩阵键盘联合使用的驱动,其本质上就是数码管驱动和矩阵键盘驱动的简单整合,....
当前接口中的am_hc595_handle_t 类型为void *类型,最终,其需要是指向对象的指针....
在接口实现中,没有与硬件相关的实现代码,仅仅是简单的调用了抽象方法。抽象方法需要由具体的温度采集设备....
数码管的各个段可以组合显示出多种图形,使用该函数可以自定义字符的解码函数
由于表达式中的数组名data可以被解释为指针,即data的类型为指向int [2]的指针类型int ....
为了便于快速开发,在AW824P2EF 内部已经将LPC824 的串口1与内置的zigbee 芯片的....
ZLG 为用户提供了大量标准的外设驱动与相关的协议组件,意在建立完整的生态系统。无论你选择什么MCU....
在日深圳举行的“‘名家芯思维’之2017年物联网核心技术和应用国际研讨会”上,周立功....
在使用SPI 通用接口前,必须先完成SPI 的初始化,以获取标准的SPI 实例句柄。LPC82x支持....
物联网作为我国战略性新兴产业的重要组成部分,在加快经济发展、促进产业转型升级、服务社会民生方面正发挥....
第四届中国物联网大会将于日在深圳召开,ZLG集团创始人周立功教授及ZLG致远电子工....
。虽然大多数开发者都很勤奋,但其奋斗目标不是企业和个人收益最大化,而是以学习与MCU和zigbee&....
逻辑链接控制子层(LLC)、介质访问控制子层(MAC)、物理编码子层(PCS)。其中LLC包括接受过....
虽然使用单一的回调机制可以实现按键管理,但是,却使得按键检测模块的职责变得不单一,其不仅要处理与硬件....
在这种情况下,应尽可能地将相应功能设计为异步模式,即启动软件定时器,设定蜂鸣器鸣叫时间,打开蜂鸣器,....
《面向AMetal框架与接口的编程(上)》一书对AMetal框架进行了详细介绍,通过阅读这本书,你可....
若用户不需要使用蜂鸣器,为了节省内存空间,可以将工程配置文件中的AM_CFG_BUZZER_ENAB....
周立功教授数年之心血之作《程序设计与数据结构》以及《面向AMetal框架与接口的编程(上)》,电子版....
由于在处理动作时,不需要任何数据,它是一个只有方法,没有属性的动作类,因此没有刻意使用结构体为其定义....
第一大电子技术学习平台
《面向AMetal框架与接口的编程(上)》一书对AMetal框架进行了详细介绍,通过阅读这本书,你可....
自动机是计算机的简单理论模型,通常将自动机分为有限自动机和图灵机。尽管有限自动机更简单,但在定义图灵....
实际上,在树的每一层次都是分叉形式,如果任意选取树中的一个结点和它的子树,所得到的部分都符合树的定义....
Nand Flash存储器是Flash存储器的一种,为固态大容量内存的实现提供了廉价有效的解决方案。....
将具有相同数据结构(属性)和行为(操作)的对象归在一起为一个类,属于类的任何对象都共享该类的所有属性....
LPC546xx系列MCU基于Cortex-M4内核而构建,具有极高的灵活性和性能可扩展性,可提供高....
在该函数的实现中,需要释放程序中分配的所有空间,主要包括添加记录时分配的结点空间,链表头结点数组空间....
作为信息管理系统,首先要能够存储学生记录,这上万条记录如何存储呢?简单地,可以使用一段连续的内存存储....
随着科技时代发展,锁也发生了多次的更新换代,智能锁是指区别于传统机械锁,在用户识别、安全性、管理性方....
显然,迭代器是一种更灵活的遍历行为,它可以按任意顺序访问容器中的元素,而且不会暴露容器的内部结构。
在实际使用中,仅有添加到链表尾部、遍历链表这些接口函数是不够的。如在结点添加函数中,当前只是按照人们....
近日周立功教授公开了数年的心血之作《程序设计与数据结构》,电子版已无偿性分享到电子工程师与高校群体下....
周立功教授数年之心血之作《程序设计与数据结构》以及《面向AMetal框架与接口的编程(上)》,电子版....
实际上循环链表,无论是头结点、尾结点还是普通结点,其本质上都是一样的。
单向链表的添加、删除操作,都必须找到当前结点的上一个结点,以便修改上一个结点的p_next指针完成相....
供应链服务
版权所有 (C) 深圳华强聚丰电子科技有限公司
电信与信息服务业务经营许可证:粤B2-博主最新文章
博主热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)请问大家,C语言函数只要加了函数声明,可以在任何地方使用,那么结构体也可以这样吗?
[问题点数:20分,结帖人yyxgs]
请问大家,C语言函数只要加了函数声明,可以在任何地方使用,那么结构体也可以这样吗?
[问题点数:20分,结帖人yyxgs]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2016年5月 总版技术专家分月排行榜第二
2016年10月优秀大版主2016年8月论坛优秀大版主
匿名用户不能发表回复!|看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?Keil 实现自动提示完成函数或结构体功能
[问题点数:40分,结帖人chengjun1991]
Keil 实现自动提示完成函数或结构体功能
[问题点数:40分,结帖人chengjun1991]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
匿名用户不能发表回复!|

我要回帖

更多关于 函数返回结构体指针 的文章

 

随机推荐