C语言泛型编程--抽象数据类型
一、数据类型:
在任何编程语言中,数据类型作为一个整体,ANSI-C包含的类型为:int、double、char……,程序员很少满意语言本身提供的数据类型,一个简单的办法就是构造类似:array、struct 或union。
那么,什么是数据类型呢?我们可以这样定义:一种数据类型是一些值的集合——通常char类型共有256不同的值,int有更多,double也包含更多的值,但是它通常和数学意义上的实数不同。
相应地,我们可以定义数据类型:包含一些值的集合,在值上面添加一些操作。通常,这些值都是计算机可以表示,同时对其的操作或多或少反应了可行的硬件指令。ANCI-C中的int类型在这方面表现得不是很好:在不同的机器上有不同的值,并且算术右移等操作也可能不同。
例如,通常我们定义一个线性结构的数据结构如下:
typedef struct node {
struct node *next;
...information...
} node;
并且我们定义如下的操作:
node * head(node * elt, const node * tail);
二、抽象数据类型:
当我们没有向用户展现具体实现,称为抽象数据类型,比如,我们可以从一个队列中移除一个元素,同事也可以按照一定的顺序向其中添加一个元素。
抽象数据类型给程序员提供了最大的灵活性,因为定义中不包含具体的实现,我们可以很自由地选择任何简单高效的实现。
抽象数据类型满足好的编程原则:信息隐藏与分治策略
代表数据项的信息只展现给需要知道的人:对程序员但不对用户。
通过抽象数据类型,我们可以方便地隔离程序的制定与实现:以自己的方式将一个大的任务拆成小的模块
三、例子--Set
我们怎样构建一个抽象数据类型呢?一个集合set包含如下操作:add, find, drop……,它们将提供集合一个元素并且会返回添加的元素。find操作被用作告诉我们是否某一个元素在集合内。
这样看来,set是一个抽象数据类型,声明我们对set的操作,从一个Set.h头文件开始:
#ifndef SET_H
#define SET_H
extern const void * Set;
void * add (void * set, const void * element);
void * find (const void * set, const void * element);
void * drop (void * set, const void * element);
int contains (const void * set, const void * element);
unsigned count (const void * set);
#endif
Set将在某种程度上展示我们在sets上的操作,add( )向set中添加一个元素,返回是否已经存在于set或是添加成功,find( )在set中寻找一个元素,返回位置或是空指针。drop( )定位一个元素并从set中移除,返回移除元素。contains( )将find( )的结果转换为一个具体的值。
通用指针void*贯穿始终,一方面,通过它可以隐藏set的一些细节,另一方面,它允许我们虚拟的传递任何的类型给add( )以及其他的函数。
四、内存管理
如何获取一个set集合?Set是一个指针,并不是通过typedef定义的类型,结果,我们不能把Set类型定义为局部变量或是全局变量。相反,我们只能通过使用指针指向sets与其中的元素,在new.h中定义如下代码:
#ifndef NEW_H
#define NEW_H
void * new (const void * type, ...);
void delete (void * item);
#endif
五、Object
假如我们打算收集set中的任何感兴趣的数据,需要另一种数据类型Object,在Object.h中描述如下:
#ifndef OBJECT_H
#define OBJECT_H
extern const void * Object; /* new(Object); */
int differ (const void * a, const void * b);
#endif
六、应用
通过上述头文件,可以写出如下的应用main.c :
#include <stdio.h> #include "new.h"
#include "Object.h"
#include "Set.h" int main ()
{ void * s = new(Set);
void * a = add(s, new(Object));
void * b = add(s, new(Object));
void * c = new(Object); if (contains(s, a) && contains(s, b))
puts("ok"); if (contains(s, c))
puts("contains?"); if (differ(a, add(s, a)))
puts("differ?"); if (contains(s, drop(s, a)))
puts("drop?"); delete(drop(s, b));
delete(drop(s, c)); return ;
}
七、实现
下面将逐一实现本文所有头文件中声明的函数:
实现--Set
main.c 可以成功编译,但是在编译和执行程序之前,我们必须实现抽象数据类型和内存管理,如果一个对象不储存任何信息,并且每一个对象都至少属于一个set,那么我们可以用一个唯一的较小的正整数值来表示对象和每一个set,而这些正整数值可以使用一个数组heap[ ]中的索引来表示。
如果一个对象是set的成员,对应的数组元素包含代表set的整数值。
Sets和对象具有相同的展示,new( )不会在意type的类型描述,它将返回heap[ ]中值为0的元素,代码如下:
#if ! defined MANY || MANY < 1
#define MANY 10
#endif static int heap [MANY]; void * new (const void * type, ...)
{ int * p; /* & heap[1..] */ for (p = heap + ; p < heap + MANY; ++ p)
if (! * p)
break;
assert(p < heap + MANY);
* p = MANY;
return p;
}
使用0来标记heap[ ]中的有效元素,结果,我们不能返回指向heap[0]的指针----假如是set,其成员可以获得0索引。new()可能越界,可以使用assert()来避免。elete()必须小心null指针,一个heap[]元素通过被设置为0从而被回收:
void delete (void * _item)
{ int * item = _item; if (item)
{ assert(item > heap && item < heap + MANY);
* item = ;
}
}
注:我们必须使用统一的方式来处理通用指针,于是我们使用在变量名前加下划线前缀的方法,只是用来初始化我们期待的类型并且名字接近的局部变量。
一个set有所包含的的对象表示:每一个元素指向set,假如一个元素包含MANY,就可以添加到set,否则,说明set中已经包含。
void * add (void * _set, const void * _element)
{ int * set = _set;
const int * element = _element; assert(set > heap && set < heap + MANY);
assert(* set == MANY);
assert(element > heap && element < heap + MANY); if (* element == MANY)
* (int *) element = set - heap;
else
assert(* element == set - heap); return (void *) element;
}
其他的函数就简单了,find( ) 仅仅用来判断set中是否包含有下划线前缀的变量名元素:
void * find (const void * _set, const void * _element)
{ const int * set = _set;
const int * element = _element; assert(set > heap && set < heap + MANY);
assert(* set == MANY);
assert(element > heap && element < heap + MANY);
assert(* element); return * element == set - heap ? (void *) element : ;
}
contains( )将find( ) 得到的结果转换为一个真值:
int contains (const void * _set, const void * _element)
{
return find(_set, _element) != ;
}
drop( ) 依赖find( )函数来检查要删除的元素是否在set中,若是,则通过将相应的对象元素的值标记为MANY:
void * drop (void * _set, const void * _element)
{ int * element = find(_set, _element); if (element)
* element = MANY;
return element;
}
接着提供了一个判断两个对象是否相等的函数differ( ):
int differ (const void * a, const void * b)
{
return a != b;
}
完整的Set.c源代码如下:
#include <assert.h>
#include <stdio.h> #include "new.h"
#include "Set.h"
#include "Object.h" const void * Set;
const void * Object; #if ! defined MANY || MANY < 1
#define MANY 10
#endif static int heap [MANY]; void * new (const void * type, ...)
{ int * p; /* & heap[1..] */ for (p = heap + ; p < heap + MANY; ++ p)
if (! * p)
break;
assert(p < heap + MANY);
* p = MANY;
return p;
} void delete (void * _item)
{ int * item = _item; if (item)
{ assert(item > heap && item < heap + MANY);
* item = ;
}
} void * add (void * _set, const void * _element)
{ int * set = _set;
const int * element = _element; assert(set > heap && set < heap + MANY);
assert(* set == MANY);
assert(element > heap && element < heap + MANY); if (* element == MANY)
* (int *) element = set - heap;
else
assert(* element == set - heap); return (void *) element;
} void * find (const void * _set, const void * _element)
{ const int * set = _set;
const int * element = _element; assert(set > heap && set < heap + MANY);
assert(* set == MANY);
assert(element > heap && element < heap + MANY);
assert(* element); return * element == set - heap ? (void *) element : ;
} int contains (const void * _set, const void * _element)
{
return find(_set, _element) != ;
} void * drop (void * _set, const void * _element)
{ int * element = find(_set, _element); if (element)
* element = MANY;
return element;
} int differ (const void * a, const void * b)
{
return a != b;
}
C语言泛型编程--抽象数据类型的更多相关文章
- 采用C/C++语言如何实现复数抽象数据类型Complex
记录一下! 采用C/C++语言如何实现复数抽象数据类型Complex #include <stdio.h> typedef struct Complex { double e1; // 实 ...
- C语言抽象数据类型ADT
根据编程的问题匹配合适的数据类型.数据项连接构成了链表,定义了一个结构代表单独的项.设计了一些方法把一系列结构构成一个链表.本质上,我们使用C语言的功能设计了一种符合程序要求的新的数据类型.但是上述的 ...
- 数据结构 集合_集合(数学)抽象数据类型的C语言实现
链表是实现集合的一种理想的方式.将List以typedef的方式重命名为Set.这样做能保留链表简洁的特性,还能使集合具有了一些多态的特性. 使用这种方法的最大好处就是可以使用list_next来遍历 ...
- 集合抽象数据类型的C语言实现
链表是实现集合的一种理想的方式.将List以typedef的方式重命名为Set.这样做能保留链表简洁的特性,还能使集合具有了一些多态的特性. 使用这种方法的最大好处就是可以使用list_next来遍历 ...
- ORACLE抽象数据类型
ORACLE抽象数据类型 *抽象数据类型*/1,抽象数据类型 概念包含一个或多个子类型的数据类型不局限于ORACLE的标准数据类型可以用于其他数据类型中 2,创建抽象数据类型 的语法(必须用NOT F ...
- 【Python&数据结构】 抽象数据类型 Python类机制和异常
这篇是<数据结构与算法Python语言描述>的笔记,但是大头在Python类机制和面向对象编程的说明上面.我也不知道该放什么分类了..总之之前也没怎么认真接触过基于类而不是独立函数的Pyt ...
- DS博客作业01--日期抽象数据类型设计与实现
1.思维导图及学习体会 1.1第一章绪论知识点思维导图 1.2 学习体会 这次博客园和大作业是我在编程学习中的有意义的进步,第一次尝试使用vs,并且通过同学的一些网站的推荐,和热心同学的帮忙,简单学会 ...
- 抽象数据类型(ADT)
抽象数据类型(Abstract Data Type,ADT)是指一个数学模型以及定义在这个模型上的一组操作.抽象数据类型的定义仅仅取决于它的一组逻辑特性,而与它在计算机中的表示和实现无关. 例如,in ...
- Go 语言的基本数据类型
Go 语言的基本数据类型 0)变量声明 var 变量名字 类型 = 表达式 例: 其中“类型”或“= 表达式”两个部分可以省略其中的一个. 1)根据初始化表达式来推导类型信息 2)默认值初始化为0. ...
随机推荐
- Android应用安全之Android APP通用型拒绝服务漏洞
0xr0ot和Xbalien交流所有可能导致应用拒绝服务的异常类型时,发现了一处通用的本地拒绝服务漏洞.该通用型本地拒绝服务可以造成大面积的app拒绝服务. 针对序列化对象而出现的拒绝服务主要是由于应 ...
- MySQL批量SQL插入性能优化
对于一些数据量较大的系统,数据库面临的问题除了查询效率低下,还有就是数据入库时间长.特别像报表系统,每天花费在数据导入上的时间可能会长达几个小时或十几个小时之久.因此,优化数据库插入性能是很有意义的. ...
- mysql数据库事件调度(Event)
mysql中的事件调度器可以定时对数据库增加,删除和执行操作,相当于数据库中的临时触发器,与Linux系统中的执行计划任务一样,这样就可以大大降低工作量. 1.开启事件调度器 [root@node1 ...
- AutoMapper用法一瞥
前段时候做个小项目用到了autoMapper(参考了NOP里的用法),感觉还行,用起来挺方便的.首先复杂的东西我就不说了,至于详细的教程我也就不写了,比较园子里有很多这样的文章,我就分享下,在项目中实 ...
- ECMAScript 6中的数组操作方法
本文介绍ECMAScript 6即将带给我们新的数组操作方法,以及在怎样在现有浏览器应用这些新的数组特性. Note: 我将使用交替使用构造器(constructor)和类(class)两个术语. 类 ...
- 编辑器之神VIM 总结(一) 基础部分
版本号 说明 作者 日期 1.0 vim基础知识 Sky Wang 2013/06/19 概要 vim和emacs,一个是编辑器之神,一个是神一样的编辑器.他们被称是UNIX系统下的 ...
- Odyssey.js – 创建互动的,有吸引力的故事
Odyssey.js 是一个开源工具,它可以让你的地图,叙述和其他多媒体结合成一个美丽的故事.创建新的故事很简单,要求无非是一个现代的 Web 浏览器和一个好的想法.你可以使用现成的模板来控制和设计精 ...
- iOS-nil,Nil,NULL的区别
一.简述 1.nil用来给对象赋值(Objective-C中的任何对象都属于id类型) 2.NULL则给任何指针赋值,NULL和nil不能互换 3.nil用于类指针赋值(在Objective-C中类是 ...
- Velocity魔法堂系列三:模板与宿主环境通信
一.前言 Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力.而且Velocity被移植到不 ...
- Gradle学习系列之四——增量式构建
在本系列的上篇文章中,我们讲到了如何读懂Gradle的语法,在本篇文章中,我们将讲到增量式地构建项目. 请通过以下方式下载本系列文章的Github示例代码: git clone https://git ...