C语言接口与实现实例
一个模块有两部分组成:接口和实现。接口指明模块要做什么,它声明了使用该模块的代码可用的标识符、类型和例程,实现指明模块是如何完成其接口声明的目标的,一个给定的模块通常只有一个接口,但是可能会有许多种实现能够提供接口所指定的功能。每个实现可能使用不同的算法和数据结构,但是它们都必须符合接口所给出的使用说明。客户调用程序是使用某个模块的一段代码,客户调用程序导入接口,而实现导出接口。由于多个客户调用程序是共享接口和实现的,因此使用实现的目标代码避免了不必要的代码重复,同时也有助于避免错误,因为接口和实现只需一次编写和调试就可多次使用。
欢迎关注我的个人博客:www.wuyudong.com, 更多精彩文章与您分享
接口
接口只需要指明客户调用程序可能使用的标识符即可,应尽可能地隐藏一些无关的表示细节和算法,这样客户调用程序可以不必依赖于特定的实现细节。这种客户调用程序和实现之间的依赖--耦合----可能会在实现改变时引起错误,当这种依赖性埋藏在一些关于实现隐藏的或是不明确的假设中时,这些错误可能很难修复,因此一个设计良好且描述精确的接口应该尽量减少耦合。
C语言对接口和实现的分离只提供最基本的支持,但是简单的约定能给接口/实现方法论带来巨大的好处。在C中,接口在头文件声明,头文件声明了客户调用程序可以使用的宏、类型、数据结构、变量以及例程。用户使用C语言的预处理指令#include导入接口。
下面的例子说明了本篇文章的接口中所使用的一些约定、接口:
extern int Arith_max(int x, int y);
extern int Arith_min(int x, int y);
extern int Arith_div(int x, int y);
extern int Arith_mod(int x, int y);
extern int Arith_ceiling(int x, int y);
extern int Arith_floor (int x, int y);
arith.h
该接口的名字为Arith,接口头文件也相应地命名为arith.h,接口的名字以前缀的形式出现在接口的每个标识符中。模块名不仅提供了合适的前缀,而且还有助于整理客户调用程序代码。
Arith接口还提供了一些标准C函数库中没有但是很有用的函数,并为出发和取模提供了良好的定义,而标准C中并没有给出这些操作的定义和只提供基于实现的定义。
实现
一个实现导出一个接口,它定义了必要的变量和函数以提供接口所规定的功能,在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的最大整数
完整实现代码如下:
#include "arith.h"
int Arith_max(int x, int y) {
return x > y ? x : y;
}
int Arith_min(int x, int y) {
return x > y ? y : x;
}
int Arith_div(int x, int y) {
if (-/ == -
&& (x < ) != (y < ) && x%y != )
return x/y - ;
else
return x/y;
}
int Arith_mod(int x, int y) {
if (-/ == -
&& (x < ) != (y < ) && x%y != )
return x%y + y;
else
return x%y;
}
int Arith_floor(int x, int y) {
return Arith_div(x, y);
}
int Arith_ceiling(int x, int y) {
return Arith_div(x, y) + (x%y != );
}
arith.c
抽象数据类型
抽象数据类型(abstract data type,ADT)是一个定义了数据类型以及基于该类型值提供的各种操作的接口
一个高级类型是抽象的,因为接口隐藏了它的表示细节,以免客户调用程序依赖这些细节。下面是一个抽象数据类型(ADT)的规范化例子--堆栈,它定义了该类型以及五种操作:
#ifndef STACK_INCLUDED
#define STACK_INCLUDED
#define T Stack_T
typedef struct T *T;
extern T Stack_new (void);
extern int Stack_empty(T stk);
extern void Stack_push (T stk, void *x);
extern void *Stack_pop (T stk);
extern void Stack_free (T *stk);
#undef T
#endif
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 = ;
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 == ;
}
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 > );
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)->head; t; t = 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 = ;
stk->head = NULL;
return stk;
}
int Stack_empty(T stk) {
assert(stk);
return stk->count == ;
}
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 > );
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)->head; t; t = u) {
u = t->link;
FREE(t);
}
FREE(*stk);
}
stack.c
参考资料
《C语言接口与实现--创建可重用软件的技术》
C语言接口与实现实例的更多相关文章
- C语言与MATLAB接口 编程与实例 李传军编着
罗列一下以前自己学习C语言与MATLAB混编的笔记,顺便复习一遍. <C语言与MATLAB接口 编程与实例 李传军编着>(未看完,目前看到P106) 目录P4-8 ************ ...
- 【python3+request】python3+requests接口自动化测试框架实例详解教程
转自:https://my.oschina.net/u/3041656/blog/820023 [python3+request]python3+requests接口自动化测试框架实例详解教程 前段时 ...
- python+requests接口自动化测试框架实例详解
python+requests接口自动化测试框架实例详解 转自https://my.oschina.net/u/3041656/blog/820023 摘要: python + requests实 ...
- GO语言学习(十八)Go 语言接口
Go 语言接口 Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口. 实例 /* 定义接口 */ type interface ...
- C语言学习书籍推荐《C语言接口与实现:创建可重用软件的技术》下载
<C语言接口与实现:创建可重用软件的技术>概念清晰.实例详尽,是一本有关设计.实现和有效使用C语言库函数,掌握创建可重用C语言软件模块技术的参考指南.书中提供了大量实例,重在阐述如何用一种 ...
- 07. Go 语言接口
Go 语言接口 接口本身是调用方和实现方均需要遵守的一种协议,大家按照统一的方法命名参数类型和数量来协调逻辑处理的过程. Go 语言中使用组合实现对象特性的描述.对象的内部使用结构体内嵌组合对象应该具 ...
- python调用C语言接口
python调用C语言接口 注:本文所有示例介绍基于linux平台 在底层开发中,一般是使用C或者C++,但是有时候为了开发效率或者在写测试脚本的时候,会经常使用到python,所以这就涉及到一个问题 ...
- 【转载】ANSYS的APDL与C语言混合编程(实例)
原文地址:http://www.cnblogs.com/lyq105/archive/2010/05/04/1727557.html 本文讨论的不是利用C语言为ANSYS写扩展(或者说是用户子程序), ...
- Swift中对C语言接口缓存的使用以及数组、字符串转为指针类型的方法
由于Swift编程语言属于上层编程语言,而Swift中由于为了低层的高性能计算接口,所以往往需要C语言中的指针类型,由此,在Swift编程语言刚诞生的时候就有了UnsafePointer与Unsafe ...
随机推荐
- How to change statusbar text color to dark on android 4.4
Because I haven't enough votes, so post picture at here, thank you. Almost 2 weeks ago, I was search ...
- MyBatis自定义数据映射TypeHandler
从网上看到的帖子,感觉内容非常好,拷过来的(不愿意转载,不然被作者删除了,这么好的帖子就看不到了). 原文:http://my.oschina.net/amoshuang/blog/134199 在M ...
- 如何自学Android--转
原文地址:http://blog.csdn.net/lavor_zl/article/details/51217319 1. Java知识储备 本知识点不做重点讲解: 对于有基础的同学推荐看<J ...
- Node.js Web 开发框架大全《静态文件服务器篇》
这篇文章与大家分享优秀的 Node.js 静态服务器模块.Node 是一个服务器端 JavaScript 解释器,它将改变服务器应该如何工作的概念.它的目标是帮助程序员构建高度可伸缩的应用程序,编写能 ...
- OpenGL开发环境配置-Windows/MinGW/Clion/CMake
因为某些原因,不想用过于臃肿的VS了,转而使用常用的jetbrains的CLion,Clion沿袭了jetbrans的优良传统,基本代码提示功能还是比较好的,不过就是对于windows不熟悉cmake ...
- json,serialize,msgpack比较
速度 在redis中存入同样的压缩数据,取操作执行两个操作: 1 从redis中取 2 解压 3 统一json压缩后放出 ab测试: [yejianfeng@openstack ~/httpd/u ...
- Velocity魔法堂系列二:VTL语法详解
一.前言 Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力.而且Velocity被移植到不 ...
- ListBox实现拖拽排序功能
1.拖拽需要实现的事件包括: PreviewMouseLeftButtonDown LBoxSort_OnDrop 具体实现如下: private void LBoxSort_OnPreviewMou ...
- Winform调用QQ发信息并且开机启动 (开源)
前言 公司CS系统需要加入启动qq从winform调用qq聊天窗口的功能,前提是需要将聊天者的QQ号码作为参数传递到函数中,一直没有搞过,正好很感兴趣,就折腾,Winform调用qq,我想肯定是需要一 ...
- Chrome浏览器的Timing分析
以W3C网站为例: Stalled是浏览器得到要发出这个请求的指令,到请求可以发出的等待时间,一般是代理协商.以及等待可复用的TCP连接释放的时间,不包括DNS查询.建立TCP连接等时间等. SSL( ...