2022年BREW实现原理[收 .pdf

上传人:Che****ry 文档编号:27254074 上传时间:2022-07-23 格式:PDF 页数:25 大小:536.96KB
返回 下载 相关 举报
2022年BREW实现原理[收 .pdf_第1页
第1页 / 共25页
2022年BREW实现原理[收 .pdf_第2页
第2页 / 共25页
点击查看更多>>
资源描述

《2022年BREW实现原理[收 .pdf》由会员分享,可在线阅读,更多相关《2022年BREW实现原理[收 .pdf(25页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、第 1 页 共 25 页深度剖析BREW实现原理 BREW原理我们 PC 的 Windows操作系统功能是如此的强大,以至于我们可以获得任何一个我们想要的程序,任何一个人都可以为Windows开发应用程序,或者给自己,或者为他人。而这一切源于操作系统的开放性和硬件平台的不断发展,尤其是存储器的发展,使得我们在编写程序的时候不必在意需要多大的存储空间了。然而,嵌入式系统可就没那么幸运了。至今为止,在嵌入式系统里仍然没有一个能够像 Windows这样应用如此广泛的操作系统,也没有可以不考虑存储空间的硬件平台。在数以亿计的嵌入式设备使用者中,都还在用着一成不变的应用程序,单调同时也令人乏味。我们能不

2、能也像在Windows下面一样,在嵌入式系统中可以安装应用程序呢?应该怎样克服嵌入式系统的限制而实现这个功能呢?有梦想才会不断的追求!我们知道,在Windows中程序都是以文件的形式存储在文件系统中的,然后通过操作系统控制这些程序的运行,我们可以说它的程序是“ 分散式 ” 的。而在嵌入式系统中通常是将程序烧录在一个Flash芯片中,文件系统在另一个Flash 芯片中(也可以二者在同一个芯片中),CPU 是直接从程序Flash芯片中读取指令执行的,没有经过文件系统,我们可以叫这种程序是“ 一体式 ” 的。 Windows的“ 分散式 ” 程序体通过文件的形式存在, 可以把程序的不同部分分割成不同

3、的文件,当我们只需要更新一个模块内容的时候,只更新这个文件就可以了。熟悉它的朋友们可能已经知道了,这个文件就是在Windows操作系统中的DLL 文件。这样的方式可以很容易的实现程序分发,这给了我们一个很好的启示: 嵌入式系统中也有文件系统,把程序放在文件系统里不就可以了吗?真是个好主意!在我们庆幸找到了好方法的时候,问题不偏不倚地出现了:系统如何运行文件系统中的程序, 文件中的程序又如何调用平台中的函数?要实现“ 分散式 ” 的程序运行, 这两个问题是必须要解决的, 而其中第二个问题就更为重要了。或许您现在还不是十分的明白这些问题的意义,不要着急,这一章里我会逐一的向您讲解如何理解并解决这两

4、个问题。当然,现在我们知道 BREW 已经在嵌入式系统中解决了这两个问题,从现在开始就让我们沿着开发者的足迹去追寻BREW 的本质吧。1. 平台的作用如果想要清楚的了解我们在嵌入式系统中所面临的问题,那么我们就首先需要了解“分散式”系统的结构。一个“分散式”系统需要有三个部分组成:平台、软件开发工具包(SDK : Software Development Kit )和应用程序。“分散式”应用程序的运行需要平台的支持,就像是 DLL文件只有在Windows 操作系统平台下才有作用,而到了 Linux 平台则不起任何作用一样; 应用程序则通过SDK进行开发, 开发出的源程序经过编译之后可以运行在运

5、行平台之上。 平台又分为开发平台和运行平台,开发平台是SDK运行的平台, 用来开发可以在运行平台上运行的应用程序,对于一些系统还会提供模拟运行平台的模拟器,以便于在没有显而易见的运行设备的时候也可以看到开发的效果;运行平台是应用程序运行的平台,它提供应用程序运行的环境,同时肩负着控制应用程序的作用。开发平台和运行平台可以是同一个平台,也可以两个不同的平台,比如现在的Windows平台的应用就可以使用VC等工具开发基于 Windows 的应用程序,而BREW SDK 则是运行在Windows环境下,但应用程序却在名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - -

6、 - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 25 页 - - - - - - - - - 第 2 页 共 25 页嵌入式系统中运行。它们之间的关系如下图:图 1 分散式系统结构图从这个图中我们可以看出,SDK需要使用运行平台的接口声明来开发应用程序,运行平台负责根据用户的输入启动应用程序,而应用程序则通过运行平台的接口调用运行平台的函数库来实现功能,我们的问题主要集中在应用程序和运行平台的互动关系上。从前面的编译器基础一章中我们可以知道,在固定链接的模式下,各个函数的地址是固定的, 我们可以在同一个映像文件中调用任何函数。而存储在文件系统中的程序就不

7、一样了,文件系统中的程序没有固定的位置并且地址也不连续,我们该怎样实现应用程序的启动呢?可选的方案就是将应用程序复制到一个连续的内存块中去,然后在内存中执行程序。在这里需要特别的说明一下,在PC的 Windows操作系统中, Windows将全部的程序载入内存中运行,并且其中包含了复杂的内存管理功能,但是在嵌入式系统中通常程序是在Flash芯片中运行的,只有可读写的数据是放在RAM 中的,具体的细节可以参考编译器基础一章。BREW 主要是应用程序在嵌入式系统中的,因此将程序复制到内存中执行是需要特殊处理的。这个特殊的处理就是我们所面临的第一个问题了系统如何运行文件系统中的程序。在我一开始理解B

8、REW 的时候,我曾经认为系统如何运行存在于文件系统中的程序是我们所面临的主要问题,但当深入BREW 内部的时候发现根本不是这么回事。现在我们可以假设运行平台分配了一个足够大的内存,这块内存地址是已知的,可以想象的到我们可以从这个地址开始执行程序。现在我们暂时忽略那些特殊的处理,文件系统中的这个程序现在正在运行, 就像在 Flash 中的程序一样的在运行。从理论上来讲这个是行得通的,因此系统如何运行文件系统中的程序的问题并不是我们所面临的难题。实际上BREW 也是按照这个思路做的,只是实现时还有细节的东西在,关于这些细节我们将在Shell内幕一章进行详细的介绍。进一步的,假设现在程序运行到了需

9、要调用平台函数的时候了,问题就来了,由于当前的应用程序是开发者使用SDK开发的, 就像平台不知道应用程序的地址一样,应用程序也不知道平台的函数地址,因此,我们现在面临的问题是怎么能够知道应用程序中所调用的平台函数的地址。SDK可以提供运行平台中的每个函数的地址吗?可以提供,但是行不通。因为平台是会经常升级的,导致每个函数的链接地址不固定,如果由 SDK提供所有函数的地址带来的问题是,只要运行平台一升级,那么 SDK和应用程序都需要同时升级。如果这样的话我们的分散式程序就不能实现“分散式”的升级了,这种程序的运行方式也就没有任何意义了。看来我们还要寻找更为高级的方法,这种方法要能够提供一种应用程

10、序与运行平台之名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 25 页 - - - - - - - - - 第 3 页 共 25 页间无关的机制。 我们现在所需要的这个“机制”就是第二个问题了文件系统中的程序如何调用平台的函数。从分散式系统结构图中我们可以看到,SDK使用的是运行平台的接口声明,应用程序调用真正的运行平台接口。或者换句话说开发过程中使用运行平台的接口声明,而在运行时应用程序使用真正的二进制接口,并且在二进制层面调用接口函数。那么,现在无论是SDK还是应用程

11、序都与接口相关,那么,可以想象的到的解决第二个问题的方式就是让接口和接口实现之间分离。接口与实现间分离的方法就是BREW 的核心,也是接下来我们主要阐述的议题。2. 软件分发和 C语言为了更好的理解“实现接口和实现分离”所面临的问题,让我们先来看看通常的C语言软件库是如何分发的,这对于我们的理解非常有用。为了能够更加清楚地理解问题,我们在接下来几节的论述中不会仅仅局限于嵌入式系统,因为同样的问题也存在于PC系统中,更为重要的是PC系统所面临的问题更加典型,并且这些问题在软件系统中是具有普遍性的。想象现在有一个C语言库的开发厂商,它开发了一个算法,可以在O(1) 时间效率内实现子字符串的搜索,O

12、(1) 时间效率的意思是指搜索时间为常数,与目标字符串的长度没有关系。为了实现这个功能,软件厂商生成了一个头文件FastString.h,包含如下内容:FastString.h 接口声明文件第一版#ifndef FASTSTRING_H_ #define FASTSTRING_H_ / 要求使用者不能直接使用此结构体中的内容typedef struct _IFastString char *m_pString; / 指向字符串的指针 IFastString; / 创建目标字符串对象void IFastString_CreateObject(IFastString * pIFastString,

13、 char *pStr); / 释放目标字符串对象void IFastString_Release(IFastString *pIFastString); / 获取目标字符串长度int IFastString_GetLength(IFastString *pIFastString); / 查找字符串,返回偏移量int IFastString_Find(IFastString *pIFastString, char *pFindStr); #endif / FASTSTRING_H_ 除了这个头文件之外软件厂商还提供了接口的实现文件FastString.c FastString.c 接口实现文件

14、第一版#include ”FastString.h”#include / 创建目标字符串对象名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 25 页 - - - - - - - - - 第 4 页 共 25 页void IFastString_ CreateObject (IFastString * pIFastString, char *pStr) IFastString *pMe = pIFastString; if(pMe = NULL | pStr = NULL)

15、 return; pMe-m_pString = malloc(strlen(pStr) +1); strcpy(pMe-m_pString,pStr); / 释放目标字符串对象void IFastString_Release(IFastString *pIFastString) IFastString *pMe = pIFastString; if(pMe = NULL) return; if(pMe-m_pString) free(pMe-m_pString); / 获取目标字符串长度int IFastString_GetLength(IFastString *pIFastString)

16、IFastString *pMe = pIFastString; if(pMe = NULL) return; return strlen(pMe-m_pString); / 查找字符串,返回偏移量int IFastString_Find(IFastString *pIFastString, char *pFindStr) 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 25 页 - - - - - - - - - 第 5 页 共 25 页 IFastString *pM

17、e = pIFastString; if(pMe = NULL) return; / 搜索算法省略,因为这里仅仅假设存在这样的一个搜索算法 这个接口总共有四个接口:CreateObject、Release 、GetLength 和 Find 。CreateObject用来创建 IFastString接口,从实现中我们可以看到它构建了IFastString结构体的内容。Release 用来释放由CreatObject分配的内存。GetLength 获得字符串的长度。 Find 用来在目标字符串中查找指定的字符串。在这个接口的实现中,使用了一个初始化的技巧( CreateObject和 Relea

18、se ),目的是为了再使用前构建IFastString结构体, 使用后可以通过Release 释放构建时分配的内存。一般来讲这个库的使用者会将.lib库链接到自己的工程中,通过接口声明的头文件使用库中的函数, 这是一个非常可行的做法。这样做带来的结果是库的可执行代码将成为客户应应用程序中不可分割的一部分。现在假设对于FastString库文件占用了大约16M的代码空间(这里假设为了完成O(1) 算法可能需要十分复杂的程序,并且采取了一些空间换时间的策略等,这可能占用大量的存储空间)。如图2 所示,如果现在有三个应用程序都在使用FastString库,那么每一个应用程序都将使用16M的空间来存储

19、这些代码,总共花费了48M的空间。 如果一个用户安装了这三个程序,也就是说有32M的空间浪费了,去存储了同样的FastString.lib中的代码。图 2 多个应用程序使用FastString库在这种情况下的另一个问题是,如果当前FastString库的厂商发现了程序中的缺陷,那么就没有任何办法可以替换已经存在的缺陷代码。一旦FastString的代码链接到了应用程序中, 我们就不可能在用户的设备上替换这部分的代码。因此,库厂商不得不重新为每个应用程序的开发者广播发布新的库文件,并且希望他们能够重新的编译他们的应用程序,以便能够使用新的代码。很显然,这可真是一件麻烦的事情,一旦应用程序开发者链

20、接了这个库文件,FastString便失去了模块化的特征。跟进一步说,这在嵌入式系统中是完名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 25 页 - - - - - - - - - 第 6 页 共 25 页全不可能的,在这里FastString的角色就是运行平台,我们总不能每一个应用程序都包含一个运行平台去啊。3. 动态链接解决上面问题的一种技术是使用动态链接技术(Dynamic Link )将 FastString包含起来,这种技术的典型应用是Windows 操作系统

21、中的动态链接库(DLL文件)。这种方法是将 FastString源文件编译成特殊的独立的二进制文件,并强迫 FastString将所有的接口从二进制文件中引用出去,建立相应的引出表,以便于在运行时把每个接口的名字映射到对应的二进制接口地址上。与此同时还需要为使用者生成相应的引入库,通过这个引入库FastString的使用者可以获得FastString中每个接口的符号。引入库中并没有包含实际的代码,它只是简单的包含FastString引出的符号名字。当客户链接引入库的时候,这些符号信息会加入到当前的可执行文件中,运行时动态的装载FastString二进制库文件,并在执行时调用相应的程序。当然这些

22、需要一些辅助工具的支持,例如编译器的支持等。此时应用程序的结构如图3:图3 动态链接示意图上图就是FastString在动态链接方式下的运行模式,这里面的引入库非常的小,所以可以忽略它占用的空间。在这种情况下,FastString的代码库就只需要一份了。运行时所有的应用程序调用同一个库中的内容,理论上当发现FastString中有缺陷的时候,我们可以更新 FastString二进制组件而不影响应用程序。可以看到, 我们已经迈出了重要一步,不过还没有完全解决我们所面临的问题。4. 封装性我们现在已经找到了一种可以实现动态链接的方法,那么下一个问题则与封装有关。考虑这样的情形:一个组织使用了Fas

23、tString,同时希望能够在2 个月内完成开发和测试。假设在这两个月中, 某些具有特殊的怀疑精神的开发人员打算在他们的应用程序上测试一下FastString的性能, 以便于测试O(1) 时间效率的搜索算法。令人惊讶的是Find 方法的搜索速度很快,并且与字符串的长度无关,但是GetLength 方法的速度不是很理想,因为在GetLength 方法中使用的是strlen计算字符串的长度,它查找字符串中的NULL结束符,它的算法需要遍历正个字符串的内容,因此它的执行效率是O(n) ,当字符串很长,而且调用次数很多的时候, 执行的速度很慢。 于是开发人员要求厂商提高GetLength 操作的执行速

24、度,使它在常数时间内完成。但是现在有一个问题,开发人员已经开发完成了他们的应用程序,他们不希望由于使用新的GetLength 方法而更改任何现有的程序。而且其他的厂商可能已经发布了使用这个现有版本的基于FastString的产品,从任何方面将库厂商都不应该影响这些已经面世的产品。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 25 页 - - - - - - - - - 第 7 页 共 25 页这个时候我们要查看我们的FastString的实现, 以便确定哪些可以改变,哪

25、些不可以改变。 幸运的是, 我们已经要求使用者不能直接使用IFastString结构体中的内容,假设所有的使用者都遵循了这一约定,于是我们很快的修改了GetLength 的方法, 将头文件改成了如下的方式(未修改部分未列出):FastString.h 接口声明文件第二版#ifndef FASTSTRING_H_ #define FASTSTRING_H_ / 要求使用者不能直接使用此结构体中的内容typedef struct _IFastString char *m_pString; / 指向字符串的指针int m_nLen; / 存储字符串的长度 IFastString; #endif /

26、FASTSTRING_H_ 将实现文件改成了如下方式(未修改部分未列出):FastString.c 接口实现文件第二版/ 创建目标字符串对象void IFastString_ CreateObject (IFastString * pIFastString, char *pStr) IFastString *pMe = pIFastString; if(pMe = NULL | pStr = NULL) return; pMe-m_nLen = strlen(pStr); pMe-m_pString = malloc(pMe-m_nLen +1); strcpy(pMe-m_pString,p

27、Str); / 获取目标字符串长度int IFastString_GetLength(IFastString *pIFastString) IFastString *pMe = pIFastString; if(pMe = NULL) return; return pMe-m_nLen; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 7 页,共 25 页 - - - - - - - - - 第 8 页 共 25 页很快的修改后重新发布了FastString的第二个版本。在这里一个显

28、著的改进是在CreateObject时将字符串长度存储起来了,当用户调用GetLength 方法时直接返回存储的长度。这样没有修改任何接口的内容,因此应用程序也就不需要修改了。客户收到了第二版的FastString后,替换了FastString的动态链接库,重新编译链接全部的应用程序,测试后发现不但原始代码不需要任何修改,而且 GetLength 方法的速度也大大加快了。最终这个第二版的FastString会随着这个产品而发布到用户手中。在安装应用程序的时候,第二版的FastString会将第一版的替换掉。这通常不会有问题,因为修改并没有影响公开的接口,因该只会增强原先已经安装的使用FastS

29、tring的应用程序的功能而以。请想象这样的情形,当用户更新了第二版的FastString后,运行新版的应用程序,用户惊喜的发现程序运行的速度提高了。然后用户关闭了新的应用程序,而打开了另一个以前安装的使用旧版本FastSting的应用程序。现在的FastString已经替换成了第二版,因此用户发现这个应用程序的性能也增强了。然而不久异常出现了,系统出现了未知的错误。不过没关系, 对于已经习惯了现代商业软件的人士来说,这不算什么问题,于是重新卸载并重新安装了两个应用程序,还是不起作用啊,异常依然发生!到底是怎么回事?原因在于 IFastString结构体的修改。在未修改前sizeof(IFas

30、tString) = 4,因为只有一个char *m_ pString变量(假设系统是32 位的)。修改后sizeof(IFastString) = 8,增加了 4 个字节。新版本的软件已经重新编译了,因此相应的IFastString结构体已经增加了空间。但是对于使用第一版FastString编译的应用程序来说,此时在应用程序里面分配的 IFastString的空间依然是4,于是当第一版的应用程序调用第二版FastString的时候,将本该属于其他用途的4 个字节用作了m_nLen的区间,这是十分粗暴的,产生异常也就不足为奇了。还记得前面的约定吗?我们要求FastString的使用者不可以直接

31、对IFastString结构体中的数据进行直接操作,以达到应用程序与数据结构间的无关性。但实际上这样的约定基本上不可能被遵守,因为在实际中不管出于什么样的目的,总会有些开发者直接使用结构体中的内容(这些开发者使用的使FastString的 C语言库文件形式,没有使用动态链接技术) 。加上前面的异常,这一切的根源是我们没有一个实现二进制数据封装的方式。如果现在能够有一种可以将全部的数据结构封装在FastString内部的方法就好了。C语言是灵活的,只要我们找到了问题,我们就可以实现它。于是第三版的FastString新鲜出炉了,首先是头文件:FastString.h 接口声明文件第三版#ifnd

32、ef FASTSTRING_H_ #define FASTSTRING_H_ typedef void IFastString; / 创建目标字符串对象void IFastString_CreateObject(IFastString * ppIFastString, char *pStr); / 释放目标字符串对象void IFastString_Release(IFastString *pIFastString); / 获取目标字符串长度int IFastString_GetLength(IFastString *pIFastString); 名师资料总结 - - -精品资料欢迎下载 -

33、- - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 8 页,共 25 页 - - - - - - - - - 第 9 页 共 25 页/ 查找字符串,返回偏移量int IFastString_Find(IFastString *pIFastString, char *pFindStr); #endif / FASTSTRING_H_ 接下来是实现的源文件:FastString.c 接口实现文件第三版#include ”FastString.h”#include typedef struct _CFastString char *m_

34、pString; / 指向字符串的指针int m_nLen; / 存储字符串的长度 CFastString; / 创建目标字符串对象void IFastString_CreateObject (IFastString *ppIFastString, char *pStr) CFastString *pMe = malloc(sizeof(CFastString); if(pMe = NULL | pStr = NULL) return; pMe-m_nLen = strlen(pStr); pMe-m_pString = malloc(pMe-m_nLen +1); strcpy(pMe-m_

35、pString,pStr); * ppIFastString = (IFastString *)pMe; / 释放目标字符串对象void IFastString_Release(IFastString *pIFastString) CFastString *pMe = (CFastString *)pIFastString; if(pMe = NULL) return; if(pMe-m_pString) free(pMe-m_pString); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - -

36、- - 第 9 页,共 25 页 - - - - - - - - - 第 10 页 共 25 页 free(pMe) / 获取目标字符串长度int IFastString_GetLength(IFastString *pIFastString) CFastString *pMe = (CFastString *)pIFastString; if(pMe = NULL) return; return strlen(pMe-m_pString); / 查找字符串,返回偏移量int IFastString_Find(IFastString *pIFastString, char *pFindStr)

37、 CFastString *pMe = (CFastString *)pIFastString; if(pMe = NULL) return; / 搜索算法省略,因为这里仅仅假设存在这样的一个搜索算法 在这个实现中, 我们使用了CFastString做为 FastString的内部数据结构, 定义了void 型的 IFastString类型做为接口指针传递,还有重要的一条是通过CreateObject获得数据结构的存储空间。通过这样的实现方法,我们将全部的数据类型封装在了FastString库中, 这样,无论新的还是老的应用程序,使用的都是统一的IFastString指针, 真正的数据是在 C

38、reateObject中进行创建的,也就不会出现上面的两种情况了。对于FastString的客户来说,他们所能看到的就是IFastString的 void 类型和四个接口函数,内部的CFastString的结构已经被隐藏起来了。不过这个第三版的FastString修改了接口函数CreateObject,因此不能够与前两版兼容。不过不要紧, 我们现在只是在说明一个更好的方法而已。4. 虚拟函数表封装性的本质是实现了接口与实现之间在二进制层次的分离,第三版的FastString似乎已经解决了我们所面临的第二个问题。不过现在我们的接口仍然在使用着动态链接用的名师资料总结 - - -精品资料欢迎下载

39、- - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 10 页,共 25 页 - - - - - - - - - 第 11 页 共 25 页引入库文件, 而且相应的动态链接库也需要提供由符号名到二进制函数地址映射的内容,为了支持这些特性,我们必须要修改相应的编译器才行。修改编译器,很复杂不是吗?让我们再回到嵌入式系统上来吧,ARM CPU是在嵌入式系统中使用最广泛的CPU ,因此相应的 ARM 编译器也是应用最广泛的,基本上成为了一种通用的编译器。我们怎么修改这个编译器呢?似乎难度有点大。更近一步的, 嵌入式系统中大大小小的CPU

40、有好多种, 我们也不可能把所有的这些编译器都修改了啊,看来修改编译器的可能性不大。又一次让我们体验到了理想与现实之间的差距!不过别灰心, 看看我们现在的接口,它是二进制层面上的, 我们能不能把这个引入库变成标准的C语言的头文件同时又能够实现接口与实现间的分离呢?如果真的能够实现这样的方法,那么将意味着我们可以通过通用编译器来实现动态链接的技术。这可真是令人兴奋,这个方法要比动态链接技术还要好。按照这个思路进一步分析,如果将现在的第三版FastString使用的 C头文件做为标准的接口文件, 那么将意味着各个接口需要静态的链接到应用程序中,接口和实现之间还是没有实现分离, 难道我们转了一圈又回到

41、原点了?真的又回来了,不过, 不同的是我们现在已经得到了第三版的FastString。现在我们已经知道了通过CreateObject来获得内部的空间,那么我们是否也可以通过一个函数来获得接口呢?可以,这个技术就是虚拟函数表(VTBL)。这可真是“山重水复疑无路,柳暗花明又一村”啊,先看看我们这个第四版的FastString的头文件吧:FastString.h 接口声明文件第四版#ifndef FASTSTRING_H_ #define FASTSTRING_H_ typedef struct _IFastString IFastString; typedef struct _IFastStri

42、ngVtbl IFastStringVtbl; typedef void (*PFNCreateObject)( IFastString *ppIFastString, char *pStr); struct _IFastString struct IFastStringVtbl *pvt; ; struct _IFastStringVtbl void (*Release) (IFastString *pIFastString); int (*GetLength) (IFastString *pIFastString); int (*Find) (IFastString *pIFastStri

43、ng, char *pFindStr); ; / 释放目标字符串对象#define IFASTSTRING_Release(p) (IFastString*)p-pvt)-Release(p) / 获取目标字符串长度#define IFASTSTRING_GetLength(p) (IFastString*)p-pvt)-GetLength(p) 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 11 页,共 25 页 - - - - - - - - - 第 12 页 共 25 页/

44、查找字符串,返回偏移量#define IFASTSTRING_Find(p,s) (IFastString*)p-pvt)-Find(p,s) #endif / FASTSTRING_H_ 接下来是C语言的源文件:FastString.c 接口实现文件第四版#include ”FastString.h”#include typedef struct _CFastString IFastStringVtbl *pvt; / 指向虚拟函数表的指针char *m_pString; / 指向字符串的指针int m_nLen; / 存储字符串的长度 CFastString; / 函数声明static v

45、oid IFastString_Release(IFastString *pIFastString); static int IFastString_GetLength(IFastString *pIFastString) static int IFastString_Find(IFastString *pIFastString, char *pFindStr); IFastStringVtbl gvtFastString = IFastString_Release, IFastString_GetLength, IFastString_Find ; / 创建目标字符串对象void IFast

46、String_CreateObject (IFastString *ppIFastString, char *pStr) CFastString *pMe = malloc(sizeof(CFastString); if(pMe = NULL | pStr = NULL) return; pMe-pvt = &gvtFastString; pMe-m_nLen = strlen(pStr); pMe-m_pString = malloc(pMe-m_nLen +1); strcpy(pMe-m_pString,pStr); * ppIFastString = (IFastString *)pM

47、e; / 释放目标字符串对象static void IFastString_Release(IFastString *pIFastString) 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 12 页,共 25 页 - - - - - - - - - 第 13 页 共 25 页 CFastString *pMe = (CFastString *)pIFastString; if(pMe = NULL) return; if(pMe-m_pString) free(pMe-m_pSt

48、ring); free(pMe) / 获取目标字符串长度static int IFastString_GetLength(IFastString *pIFastString) CFastString *pMe = (CFastString *)pIFastString; if(pMe = NULL) return; return strlen(pMe-m_pString); / 查找字符串,返回偏移量static int IFastString_Find(IFastString *pIFastString, char *pFindStr) CFastString *pMe = (CFastSt

49、ring *)pIFastString; if(pMe = NULL) return; / 搜索算法省略,因为这里仅仅假设存在这样的一个搜索算法 首先对这个第四版的FastString程序做一个说明。在FastString头文件中我们定义了两个类型:IFastString和 IFastStringVtbl。IFastStringVtbl类型是虚拟函数表的类型, IFastString中包含了指向虚拟函数表的指针。在接口定义的时候,我们使用了名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - -

50、第 13 页,共 25 页 - - - - - - - - - 第 14 页 共 25 页(IFastString*)p-pvt)来调用虚拟函数表中的函数指针,这也说明了如果要使用接口,必须要提供 IFastString的指针类型。可以看出Release 、GetLength 和 Find 已经实现了在C语言定义的接口与实现函数之间的分离。接着看一下源文件中的情况。CFastString结构体与第三版的FastString不同的是增加了一个IFastStringVtbl类型的指针,而且这个指针在结构体的最顶部,如果将IFastString和 CFastString对比一下我们可以发现他们都在顶

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 高考资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com