0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

周立功教你学C语言编程:结构体,使程序设计更方便——内置函数指针和嵌套结构体

AGk5_ZLG_zhiyua 来源:未知 作者:电子大兵 2017-09-06 09:27 次阅读

第二章为程序设计技术,本文为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引用该结构体的数据,并调用相应的处理函数。真正想强化的是由方法定义结构体的思想,而不是实现结构体时碰巧用到的那些数据。

2.初始化

使用名为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嵌套结构体

1.重构

随着添加一个又一个功能,处理一个又一个错误,代码的结构会逐渐退化。如果对此置之不理,这种退化最终会导致纠结不清,难以维护的混乱代码,因此需要经常性地重构代码扭转这种退化。

重构就是在不改变代码行为的前提下,对其进行一系列小的改进,旨在改进系统结构的实践活动。虽然每个改进都是微不足道的,甚至几乎不值得去做,但如果将所有的改造叠加在一起时,对系统设计和架构的改进效果是十分明显的。

在每次细微改进后,通过运行单元测试以确保改进没有造成任何破坏,然后才去做下一次改进。如此往复周而复始,每次改进后都要运行,通过这种方式保证在改进系统设计的同时系统能够正常工作。

重构是持续进行的,而不是在项目结束时、发布版本时、迭代结束时、甚至每天下班时才进行。重构是每隔一个小时或半个小时就要去做的事情,通过重构可以持续地保持尽可能干净、简单且有表现力的代码。

大量的实践证明,重复可能是软件中一切邪恶的根源,许多原则和实践规则都是为了控制与消除重复而创建的。消除重复最好的方法就是抽象,即将所有公共的函数指针移到一个单独的结构体中,创建一个通用的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)

在这里,作者并没有提供完整的代码,请读者补充完善。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 指针
    +关注

    关注

    1

    文章

    480

    浏览量

    70561
  • C语言编程
    +关注

    关注

    6

    文章

    90

    浏览量

    21106
  • 周立功
    +关注

    关注

    38

    文章

    130

    浏览量

    37628
  • 结构体
    +关注

    关注

    1

    文章

    130

    浏览量

    10841

原文标题:周立功:结构体,使程序设计更方便——内置函数指针和嵌套结构体

文章出处:【微信号:ZLG_zhiyuan,微信公众号:ZLG致远电子】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    立功教你C语言编程程序设计:这样写函数指针数组最好用

    立功教授数年之心血之作《程序设计与数据结构》以及《面向AMetal框架与接口的编程(上)》,电子版已无偿性分享到电子工程师与高校群体,在公
    的头像 发表于 08-31 14:06 6906次阅读
    <b class='flag-5'>周</b><b class='flag-5'>立功</b><b class='flag-5'>教你</b><b class='flag-5'>学</b><b class='flag-5'>C</b><b class='flag-5'>语言</b><b class='flag-5'>编程</b>与<b class='flag-5'>程序设计</b>:这样写<b class='flag-5'>函数</b><b class='flag-5'>指针</b>数组最好用

    立功教你程序设计结构:内存对齐和基本数据类型

    我们知道,数组和指针是相同类型有序数据的集合,但很多时候需要将不同类型的数据捆绑在一起作为一个整体来对待,使程序设计方便。在
    的头像 发表于 09-01 13:47 7996次阅读
    <b class='flag-5'>周</b><b class='flag-5'>立功</b><b class='flag-5'>教你</b><b class='flag-5'>学</b><b class='flag-5'>程序设计</b><b class='flag-5'>结构</b><b class='flag-5'>体</b>:内存对齐和基本数据类型

    高手们帮忙看下这个结构嵌套程序,编译无法通过

    ;<unnamed>" has no field "x"我查了一下C语言的相关书籍,问题是内层嵌套结构没有变量名,由于子
    发表于 03-15 11:08

    新书创作谈:立功教授数十年之心血力作《程序设计与数据结构

    整理出《程序设计与数据结构》这本书,其内容如同培训讲师的教案,是立功和团队的读书笔记和程序设计实践的心得。《
    发表于 05-15 18:04

    【完整资料】《程序设计与数据结构立功数十年心血力作

    ,是立功和团队的读书笔记和程序设计实践的心得。《程序设计与数据结构》重点阐述了三大方向内容。C
    发表于 05-16 16:43

    漫谈C语言结构

    变量的方法代替函数(入口参数)的重新定义就表示了结构“中用”。继续以上面的结构为例子,我们来分析。假如现在我有如下
    发表于 11-15 15:59

    单片机C语言 -- 基于结构的面向对象编程技巧

    1、Keil4 C51工程网址:2、需要一定的C语言基础,才看得懂此文。一、面向对象单片机C语言的面向对象
    发表于 02-04 21:48

    程序设计与数据结构》【完整资料】分享!

    。 在当前工程师在C语言学习时,遇到如指针函数函数指针,如何灵活应用
    发表于 08-31 16:20

    C语言结构函数指针封装示例

    C语言结构函数指针封装示例示例:#include int i, j;struct DATE{
    发表于 07-15 06:18

    C语言入门教程-指向结构指针

    指向结构指针C语言中几乎可以创建指向任何类型的指针,包括用户自定义的类型。创建
    发表于 07-29 12:04 3153次阅读

    C语言程序设计介绍

    包括:C 语言概述,算法基础,数据类型、运算符与表达式,顺序 结构程序设计,选择结构程序设计
    发表于 02-17 15:49 0次下载

    C语言程序设计教程之结构与共用的详细资料说明

    本文档详细介绍的是C语言程序设计教程之结构与共用的详细资料说明主要内容包括了:1
    发表于 03-01 17:20 11次下载
    <b class='flag-5'>C</b><b class='flag-5'>语言</b><b class='flag-5'>程序设计</b>教程之<b class='flag-5'>结构</b><b class='flag-5'>体</b>与共用<b class='flag-5'>体</b>的详细资料说明

    C程序设计基础-数组与结构

    数组,结构指针C语言里面最难的部分,如果在编程中可以熟练的运用这三样工具,会大大地提高开发
    的头像 发表于 03-02 10:10 1027次阅读
    <b class='flag-5'>C</b><b class='flag-5'>程序设计</b>基础-数组与<b class='flag-5'>结构</b><b class='flag-5'>体</b>

    C语言入门之结构指针

    C语言中,指向结构对象的指针变量既可以指向结构
    的头像 发表于 03-24 14:59 1051次阅读

    结构指针的关系

    C语言中,结构(Struct)是一种用户自定义的数据类型,它允许您将不同类型的数据项组合在一起,以便形成一个复杂的数据
    的头像 发表于 01-11 08:00 994次阅读
    <b class='flag-5'>结构</b><b class='flag-5'>体</b>与<b class='flag-5'>指针</b>的关系