zoukankan      html  css  js  c++  java
  • 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 (-13/5 == -2
        &&    (x < 0) != (y < 0) && x%y != 0)
            return x/y - 1;
        else
            return x/y;
    }
    int Arith_mod(int x, int y) {
        if (-13/5 == -2
        &&    (x < 0) != (y < 0) && x%y != 0)
            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 != 0);
    }
    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 = 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)->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 = 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)->head; t; t = u) {
            u = t->link;
            FREE(t);
        }
        FREE(*stk);
    }
    stack.c

    参考资料

    《C语言接口与实现--创建可重用软件的技术》

      

  • 相关阅读:
    《C# to IL》第一章 IL入门
    multiple users to one ec2 instance setup
    Route53 health check与 Cloudwatch alarm 没法绑定
    rsync aws ec2 pem
    通过jvm 查看死锁
    wait, notify 使用清晰讲解
    for aws associate exam
    docker 容器不能联网
    本地运行aws lambda credential 配置 (missing credential config error)
    Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?
  • 原文地址:https://www.cnblogs.com/wuyudong/p/c-interfaces-implementations.html
Copyright © 2011-2022 走看看