c语言接口与实现--第11章序列理解
2018-01-24 09:58
197 查看
本章中强调序列是本书中最有用的ADT(abstact data type)之一,尽管序列的规格相对简单,但可以用作数组、链表、栈、队列和双端队列,实现这些数据结构的ADT所需的设施通常都包含在序列中。前面这些基本是书中的原话,话不多说,上代码。
seq.h
seq.c
这本书在介绍数据结构这块的接口设计基本类似,所以我觉得我们在学习的时候需要注意总结,看别人的代码接口是如何设计的,参数设计成什么样,返回值是什么类型。
我在做开发的时候,需求很明确,但是如何在实现需求的同时,写出良好的代码和实现清晰的设计逻辑,是个很大的问题,用超生游击队小品(暴露年龄了,哈哈)的话说“憋三天憋四天,憋出个海南岛吐鲁番”,到最后功能是实现了,但代码实现混乱,冗余,可读性差,甚至自己都不想再看写过的东西,这些都是缺乏设计的表现。
现在主要针对上面的截图说一下我的理解
函数说明
创建一个Seq_T* 变量seq, 并分配hint*sizeof(void *)大小的单元格,如果hint=0,则默认创建16个单元格,即上图中的结构,此时seq->length=0,seq->head=0, seq->array.length=16,seq->array.size=sizeof(void *), seq->array.array为ALLOC返回的首地址。
往已创建的seq里面写数据,采用可变参数形式va_list实现,用Seq_addhi在seq尾部填充数据,seq->head值不变,seq->length长度递增,比如书中的例子
name=Seq_seq(“c”, “ML”, “c++”, “Icon”, “AWK”, NULL);
释放序列,seq有Seq_T *类型强制转换为Array_T*类型,然后通过Array_free函数来释放,我们看看为什么seq可以转换为Array_T*来释放,先看Array_free的实现
可以看到Seq_T只比Array_T多两个非指针变量int length和 int head,上面函数中“1”可以将seq->array数组释放掉,”2”可以将seq释放掉。
有必要说一下代码片段seq[i] 127
这里的void ** 是二级指针,从顺序结构上看直接作用于最后的array,是将array转换为void *类型的数组,我们知道数组地址可以赋值给指针变量,比如int a[], int *b; b=a; 所以void ** 是void *类型的数组,将array这段内存空间转换为数组后,可以直接用数组序号来定位某个元素。
理解了之前图中的数据结构,对于元素的增减就比较好理解了
最后说明一下expand函数,代码如下
演化结构如下图所示
seq.h
#ifndef SEQ_INCLUDED #define SEQ_INCLUDED #define T Seq_T typedef struct T *T; extern T Seq_new(int hint); extern T Seq_seq(void *x, ***); extern void Seq_free(T *seq); extern int Seq_length(T seq); extern void *Seq_get(T seq, int i); extern void *Seq_put(T seq, int i, void *x); extern void *Seq_addlo(T seq, void *x); extern void *Seq_addhi(T seq, void *x); extern void *Seq_remlo(T seq); extern void *Seq_remhi(T seq); #undef T #endif
seq.c
#include <stdlib.h> #include <stdarg.h> #include <string.h> #include "assert.h" #include "seq.h" #include "array.h" #include "arrayrep.h" #include "mem.h" #define T Seq_T struct T { struct Array_T array; int length; int head; }; T Seq_new(int hint) { T seq; assert(hint >= 0); NEW0(seq); if(hint == 0) { hint = 16; } ArrayRep_init(&seq->array, hint, sizeof(void *), ALLOC(hint*sizeof(void *))); return seq; } T Seq_seq(void *x, ...) { va_list ap; T seq = Seq_new(0); va_start(ap, x); for(; x; x=va_arg(ap, void *)) { Seq_addhi(seq, x); } va_end(ap); return seq; } void Seq_free(T *seq) { assert(seq && *seq); assert((void *)*seq == (void *)&(*seq)->array); Array_free((Array_T *)seq); } int Seq_length(T seq) { assert(seq); return seq->length; } void *Seq_get(T seq, int i) { assert(seq); assert(i>=0 && i<seq->length); return ((void **)seq->array.array)[(seq->head+i)%seq->array.length]; } void *Seq_put(T seq, int i, void *x) { void *prev; assert(seq); assert(i>=0&&i<seq->length); prev = ((void **)seq->array.array)[(seq->head+i)%seq->array.length]; ((void **)seq->array.array)[(seq->head+i)%seq->array.length] = x; return prev; } void *Seq_remhi(T seq) { int i; assert(seq); assert(seq->length > 0); i = --seq->length; return ((void **)seq->array.array)[(seq->head+i)%seq->array.length]; } void *Seq_remlo(T seq) { int i = 0; void *x; assert(seq); assert(seq->length > 0); x =((void **)seq->array.array)[(seq->head+i)%seq->array.length]; seq->head = (seq->head + 1)%seq->array.length; --seq->length; return x; } void *Seq_addhi(T seq, void *x) { int i = 0; assert(seq); if(seq->lenth == seq->array.lenth) { expand(seq); } if(--seq->head < 0) { seq->head = seq->array.lenth - 1; } seq->length++; return ((void **)seq->array.array)[(seq->head+i)%seq->array.length]=x; } static void expand(T seq) { int n = seq->array.length; Array_resize(&seq->array, 2*n); if(seq->head > 0) { void **old = &((void **)seq->array.array)[seq->head]; memcpy(old+n, old, (n-seq->head)*sizeof(void *)); seq->head += n; } }
这本书在介绍数据结构这块的接口设计基本类似,所以我觉得我们在学习的时候需要注意总结,看别人的代码接口是如何设计的,参数设计成什么样,返回值是什么类型。
我在做开发的时候,需求很明确,但是如何在实现需求的同时,写出良好的代码和实现清晰的设计逻辑,是个很大的问题,用超生游击队小品(暴露年龄了,哈哈)的话说“憋三天憋四天,憋出个海南岛吐鲁番”,到最后功能是实现了,但代码实现混乱,冗余,可读性差,甚至自己都不想再看写过的东西,这些都是缺乏设计的表现。
现在主要针对上面的截图说一下我的理解
函数说明
T Seq_new(int hint) { T seq; assert(hint >= 0); NEW0(seq); if(hint == 0) { hint = 16; } ArrayRep_init(&seq->array, hint, sizeof(void *), ALLOC(hint*sizeof(void *))); return seq; }
创建一个Seq_T* 变量seq, 并分配hint*sizeof(void *)大小的单元格,如果hint=0,则默认创建16个单元格,即上图中的结构,此时seq->length=0,seq->head=0, seq->array.length=16,seq->array.size=sizeof(void *), seq->array.array为ALLOC返回的首地址。
T Seq_seq(void *x, ...) { va_list ap; T seq = Seq_new(0); va_start(ap, x); for(; x; x=va_arg(ap, void *)) { Seq_addhi(seq, x); } va_end(ap); return seq; }
往已创建的seq里面写数据,采用可变参数形式va_list实现,用Seq_addhi在seq尾部填充数据,seq->head值不变,seq->length长度递增,比如书中的例子
name=Seq_seq(“c”, “ML”, “c++”, “Icon”, “AWK”, NULL);
void Seq_free(T *seq) { assert(seq && *seq); assert((void *)*seq == (void *)&(*seq)->array); Array_free((Array_T *)seq); }
释放序列,seq有Seq_T *类型强制转换为Array_T*类型,然后通过Array_free函数来释放,我们看看为什么seq可以转换为Array_T*来释放,先看Array_free的实现
// T 为Array_T void Array_free(T *array) { assert(array && *array); FREE((*array)->array); // 1 FREE(*array); // 2 }
可以看到Seq_T只比Array_T多两个非指针变量int length和 int head,上面函数中“1”可以将seq->array数组释放掉,”2”可以将seq释放掉。
有必要说一下代码片段seq[i] 127
((void **)seq->array.array)[(seq->head+i)%seq->array.length];
这里的void ** 是二级指针,从顺序结构上看直接作用于最后的array,是将array转换为void *类型的数组,我们知道数组地址可以赋值给指针变量,比如int a[], int *b; b=a; 所以void ** 是void *类型的数组,将array这段内存空间转换为数组后,可以直接用数组序号来定位某个元素。
理解了之前图中的数据结构,对于元素的增减就比较好理解了
void * Seq_remlo(T seq); void * Seq_remhi(T seq); void * Seq_addlo(T seq, void * x); void * Seq_addhi(T seq, void * x);
最后说明一下expand函数,代码如下
// 在add函数中有调用,当seq->length == seq->array.length时 // 证明已占用单元格数量与开辟的单元格数量相等,即所有单元格已全部占用,无剩余空间, // 如需要再加入数据,需要重新申请更大的空间 static void expand(T seq) { int n = seq->array.length; Array_resize(&seq->array, 2*n); if(seq->head > 0) { void **old = &((void **)seq->array.array)[seq->head]; memcpy(old+n, old, (n-seq->head)*sizeof(void *)); seq->head += n; } }
演化结构如下图所示
相关文章推荐
- c#打包文件解压缩 C#中使用委托、接口、匿名方法、泛型委托实现加减乘除算法 一个简单例子理解C#的协变和逆变 对于过长字符串的大小比对
- GOF23设计模式之观察者模式的理解与实现2(使用java自带的观察者类和接口)
- Go语言中接口的定义与实现
- 实现Java与C语言接口
- 字符串String类的完整实现 C++程序设计语言第11章
- 动态规划---实现输出最大公共子序列的长度以及输出最大子字符串(java语言描述)
- javaSE_8系列博客——Java语言的特性(五)--接口和继承(3)--实现接口
- WINCE多语言接口(MUI)的实现
- 自己实现MySQL CPP语言接口
- Socket接口原理及用C#语言实现
- Go语言中接口组合的实现方法
- c语言接口与实现--内存管理章节理解,含实例
- 9.6-OOP语言 对接口和抽象类的理解
- TQ2440 学习笔记—— 13、GPIO 接口【实验:用汇编语言实现】
- KMP算法中next数组的理解与算法的实现(java语言)
- myatis通过接口实现数据操作的自我理解
- 实现Java与C语言接口
- java语言基础(102)——深入理解java枚举类(自己实现枚举 VS java原生提供枚举)
- 实现Java与C语言接口
- (转)如何在linux C/C++语言中调用 sqlite 的函数接口来实现对数据库的管理