在c和指针函数一章讲到了ADT,即黑盒,书上说是用static实现的,但是我一直没搞清楚它的工作原

在c和指针函数一章讲到了ADT,即黑盒,书上说是用static实现的,但是我一直没搞清楚它的工作原,第1张

C可以用于设计和实现抽象数据类型(ADT,abstract data type),因为他可以限制函数和

数据定义的作用域。这个几千也被称为黑盒(black box )设计。抽象数据类型的基本思想

----模块具有

功能说明----模块所执行的任务

接口说明----模块的使用

模块的用户并不需要知道模块实现的任何细节,并且除了已经定义好的那些接口以外,

用户不能一任何方式访问模块。

限制对模块的访问是通过static关键字的合理使用实现的,他可以限制对那些并非

接口的函数和数据的访问。

例如:一个用于维护一个地址/电话号码列表的模块。模块必须提团搏洞供函数,根据一个制定的

名字查找地址和电话号码。但列表存储的方式依赖于具体的实现,并且这个信银茄息为模块所私有

客户并不清楚,也不必清楚。

是时间改看一个例子的时候了,下面的程序说明了这个模块的一种可能的实现方法。

头文件定义了一些由客户使用的接口。

/*filename : addrlist.h*/

/*

*地址模块的声明

*/

/*

*数据特征

*各种数据的最大长度(包括结尾的NUL字节)和地址的最大数量

*/

#define NAME_LENGTH 30

#define ADDR_LENGTH 100

#define PHONE_LENGTH 11

#define MAX_ADDRESSES 1000

/*

*接口函数

*根据给出名字,查找对应的地址

*/

char const *

lookup_address(char const *name)

/*给据给出的名字查找对应的电话号码*/

char const *

lookup_phone(char const *name)

------------------------------------------------

/*file name : addrlist.c*/

/*用于维护一个地址列表的抽象数据类型*/

#include "addrlist.h"

#include

/*

*每个地址的三个部分分别保存于三个数字化的对应元素中

*

*/

static char name[MAX_ADDRESSES][NAME_LENGTH]

static char address[MAX_ADDRESSES][ADDR_LENGTH]

static char phone[MAX_ADDRESSES][PHONE_LENGTH]

/*

*在数组中查找一塌枯个名字返回查找的位置的下标

*如查询不到则直接返回-1

*/

static int

find_entry(char const *name_to_find)

{

int entry

for(entry = 0entry <MAX_ADDRESSESentry ++)

if(strcmp(name_to_find,name[entry]) == 0)

return entry

return -1

}

/*

*给定一个名字查找并返回对应的地址

*如果名字没有找到直接返回一个NULL指针

*/

char const *

lookup_address(char const *name)

{

int entry

entry = find_entry(name)

if(entry == -1)

return NULL

else

return address[entry]

}

/*

*根据给定的名字查找并返回对应的电话号码,如名字不存在则返回NULL指针

*/

char const *

lookup_phone(char const *name)

{

int entry

entry = find_entry(name)

if (entry == -1)

return NULL

else

return phone[entry]

}

这个例子可以很好的说明黑盒的功能。黑盒通过规定特定的接口,来提供给外部用户的访问

在这个例子中,接口函数是lookup_address和lookup_phone。用户不能直接访问和模块实现有关的

数据,如数组或辅助函数find_entry,因为这些内容被声明为static。

这种黑盒的概念使实现细节与外界隔绝,这就消除了用户试图直接访问这些实现谢姐的诱惑,

这样访问模块唯一可能的方法就是通过模块所定义的接口。

这种开发模式是非常重要的,尤其是在大型项目规划中,很多时候我们只考虑接口问题,

其中具体的实现细节我们可以暂不考虑,一提高团队整体合作开发的速度。

一个模块有两部分组成:接口和实现。接口指明模块要做什么,它声明了使用该模块的代码可用的标识符、类型和例程,实现指明模块是如何完成其接口声明的目标的,一个给定的模块通常只有一个接口,但是可能会有许多种实现能够提供接口所指定的功能。每个实现可能使用不同的算法和数据结构,但是它们都必须符合接口所给出的使用说明。客户调用程序是使用某个模块的一段代码,客户调用程序导入接口,而实现导出接口。由于多个客户调用程序是共享接口和实现的,因此使用实现的目标代码避免了不必要的代码重复,同时也有助于避免错误,因为接口和实现只需一次编写和调试就可多次使用

实现

一个实现导出一个接口,它定义了必要的变量和函数以提供接口所规定的功能,在C语言中,一个实现是由一个或多个.c文件提供的,一个实现必则胡须提供其导出的接口所指定的功能。实现应包含接口的.h文件,以保证它的定义和接口的声明时一致的。

Arith_min和Arith_max返回其整型参数中的最小值和最大值:

int Arith_max(int x, int y) {

return x >y ? x : y

}

int Arith_min(int x, int y) {

return x >y ? y : x

}

Arith_div返回y除以x得到的商,Arith_mod返回相应的余数。当x与y同号的时候,Arith_div(x,y)等价于x/y,Arith_mod(x,y)等价于x%y

当x与y的符号不同的时候,C的内嵌 *** 作的返回值就取决于具体的实现:

eg.如果-13/5=2,-13%5=-3,如果-13/5=-3,-13%5=2

标准库函数总是向零取整,因此div(-13,2)=-2,Arith_div和Arith_mod的语义同样定义好了:它们总是趋近数轴的左侧取整,因此Arith_div(-13,5)=-3,Arith_div(x,y)是不超过实数z的最大整数,其中z满足z*y=x。

Arith_mod(x,y)被定义为x-y*Arith_div(x,y)。因此Arith_mod(-13,5)=-13-5*(-3)=2

函数Arith_ceiling和Arith_floor遵循类似的约定,Arith_ceiling(x,y)返回不小于实数商x/y的最小整数

Arith_floor(x,y)返回不超过实数商x/y的最大整数

完整实现代码如下:

arith.c

抽象数据类型

抽象数据类型(abstract data type,ADT)是一个定义了数据类型以及基于该类型值提供的各种 *** 作的接口

一个高级类型是抽象的,因为接口隐藏了它的表示细节,以免客户调用程序依赖这些细节。下面是一个抽象数据类型(ADT)的规范化例子--堆栈,它定磨拆义了该类型以及五种瞎盯枣 *** 作:

stack.h

实现

包含相关头文件:

#include <stddef.h>

#include "assert.h"

#include "mem.h"

#include "stack.h"

#define T Stack_T

Stack_T的内部是一个结构,该结构有个字段指向一个栈内指针的链表以及一个这些指针的计数:

struct T {

int count

struct elem {

void *x

struct elem *link

} *head

}

Stack_new分配并初始化一个新的T:

T Stack_new(void) {

T stk

NEW(stk)

stk->count = 0

stk->head = NULL

return stk

}

其中NEW是一个另一个接口中的一个分配宏指令。NEW(p)将分配该结构的一个实例,并将其指针赋给p,因此Stack_new中使用它就可以分配一个新的Stack_T

当count=0时,Stack_empty返回1,否则返回0:

int Stack_empty(T stk) {

assert(stk)

return stk->count == 0

}

assert(stk)实现了可检查的运行期错误,它禁止空指针传给Stack中的任何函数。

Stack_push和Stack_pop从stk->head所指向的链表的头部添加或移出元素:

void Stack_push(T stk, void *x) {

struct elem *t

assert(stk)

NEW(t)

t->x = x

t->link = stk->head

stk->head = t

stk->count++

}

void *Stack_pop(T stk) {

void *x

struct elem *t

assert(stk)

assert(stk->count >0)

t = stk->head

stk->head = t->link

stk->count--

x = t->x

FREE(t)

return x

}

FREE是另一个接口中定义的释放宏指令,它释放指针参数所指向的空间,然后将参数设为空指针

void Stack_free(T *stk) {

struct elem *t, *u

assert(stk &&*stk)

for (t = (*stk)->headtt = u) {

u = t->link

FREE(t)

}

FREE(*stk)

}

完整实现代码如下:

#include <stddef.h>

#include "assert.h"

#include "mem.h"

#include "stack.h"

#define T Stack_T

struct T {

int count

struct elem {

void *x

struct elem *link

} *head

}

T Stack_new(void) {

T stk

NEW(stk)

stk->count = 0

stk->head = NULL

return stk

}

int Stack_empty(T stk) {

assert(stk)

return stk->count == 0

}

void Stack_push(T stk, void *x) {

struct elem *t

assert(stk)

NEW(t)

t->x = x

t->link = stk->head

stk->head = t

stk->count++

}

void *Stack_pop(T stk) {

void *x

struct elem *t

assert(stk)

assert(stk->count >0)

t = stk->head

stk->head = t->link

stk->count--

x = t->x

FREE(t)

return x

}

void Stack_free(T *stk) {

struct elem *t, *u

assert(stk &&*stk)

for (t = (*stk)->headtt = u) {

u = t->link

FREE(t)

}

FREE(*stk)

}


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/tougao/8203016.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-14
下一篇 2023-04-14

发表评论

登录后才能评论

评论列表(0条)

保存