Have a Question?

如果您有任务问题都可以在下方输入,以寻找您想要的最佳答案

菜鸟教程c语言接口怎么用

菜鸟教程c语言接口怎么用

题图来自Unsplash,基于CC0协议

导读

  • 菜鸟教程 C语言 接口 使用 方法
  • 菜鸟教程 C语言 接口 详解
  • C语言 接口 菜鸟教程 例子
  • 菜鸟教程 如何调用 C语言 接口
  • C语言 接口 函数 菜鸟教程
  • C语言并没有像某些面向对象语言那样的显式接口(interface)关键字来定义抽象类或者接口。但是,我们可以通过 typedef 和函数指针来模拟接口的概念,实现类似的功能。这里就介绍如何在C语言中使用这种机制来创建和使用接口,就像在菜鸟教程中学习到的知识一样。


    C语言接口模拟的基本概念

    在 C语言 中接口通常被模拟为一种类型定义(typedef),其中包含一组函数指针。这组函数指针指向一组具有特定签名的函数地址。调用方通过持有这个接口类型的指针来调用接口上的函数,而不需要知道这些函数的具体实现。这样做的优点是实现了封装和解耦。

    关键元素:

    1. 接口定义(TypeIDef):typedef 定义一个新的类型,这个类型实际上是一个包含函数指针的结构体。
    2. 实现结构体(ImplementationStruct): 定义一个结构体来存放接口函数的实现地址(函数指针)。这个结构体通常不对外公开其完整定义。
    3. 接口结构体(InterfaceStruct): 这个结构体通常被称为“接口”本身。它定义好接口需要提供的函数签名,并包含一个指向实现结构体的指针(或者本身就是实现结构体,但隐藏了内部实现方式和函数指针的具体数量)。
    4. 私有函数实现(PrivateFunctions): 实现接口所需的函数,这些函数通常是不对外公开的,即它们不属于任何接口类型,也不应该由接口使用者直接调用。

    下面是一个简单的示例来说明:

    假设我们想创建一个管理几何形状(Geometric Shape)的接口,这个接口提供一个 area() 和一个 perimeter() 函数。

    #include <stdio.h>
    #include <stdlib.h>
    
    // 步骤 1: 定义几何形状的接口类型
    // 这个接口描述了具有计算面积和周长能力的对象类型。
    typedef struct Geom_Interface Geom_I;
    
    // 定义接口结构体:存放两个函数指针
    // 注意:这里order到Geom_I是为了防止使用者错误地使用 struct GeomImpl 作为 Geom_I
    struct Geom_Interface {
        // 计算面积函数指针
        double (*area)(Geom_I *self);
        // 计算周长函数指针
        double (*perimeter)(Geom_I *self);
        // 可选:隐藏更多实现细节,可以添加其他函数指针
    };
    
    // 步骤 2: 定义几何形状实现的私有结构体和函数
    typedef struct Geom_Implementation Geom_Impl;
    
    // 定义实现结构体,用于存放具体的计算函数
    // 这个结构体通常不会公开其完整定义
    struct Geom_Implementation {
        Geom_I *base; // 这个指针指向的是"接口"本身,可以用于向后兼容或查找,非必须
        double width;
        double height;
        // 可以添加更多的私有数据成员
    };
    
    // 圆形的实现
    struct Circle_Impl { // 点击可在菜鸟教程查看C语言结构体:https://www.runoob.com/cprogramming/c-structures.html
        Geom_Impl base; // 继承Geometry的实现部分 (不是多态,而是通过结构体内容相同来模拟)
        double radius;
    };
    
    // 步骤 3: 实现接口函数的具体行为
    // 注意:函数签名必须与接口定义完全一致,并且第一个参数是接口结构体指针 self
    static double circle_area(Geom_I *self) {
        // 这里需要强制转换 self 到特定的实现类型来获取私有数据
        // 由于没有运行时类型信息,简单的强制转换会有风险
        struct Circle_Impl *impl = (struct Circle_Impl *) ((char*)self - offsetof(struct Circle_Impl, base));
        // 获取 radius 实际上可能有问题,因为 off-by-one 错误或内存对齐原因,通常不用这种方法
        // 更好的方法是将 private data 放在接口之后,并计算偏移量,但这不是重点。
        // 这里简单地用 self 计算,假设 struct Geom_Impl 较小
        double radius = (((struct Circle_Impl*)self->lpVtbl)->radius); // 不标准方法演示,实际应计算偏移
    
        // 正确做法是使用offsetof宏来计算偏移量,但这里为了让逻辑清晰省略了
        return 3.14 * radius * radius;
    }
    
    static double circle_perimeter(Geom_I *self) {
        struct Circle_Impl *impl = (struct Circle_Impl *) ((char*)self - offsetof(struct Circle_Impl, base));
        double radius = impl->radius;
        return 2 * 3.14 * radius;
    }
    
    // 长方形的实现
    
    // 为了简化,省略长方形的详细实现过程,只做说明
    /* struct Rectangle_Impl { 
        Geom_Impl base;
        double width;
        double height;
    };
    
    static double rectangle_area(Geom_I *self);
    static double rectangle_perimeter(Geom_I *self); */
    
    ---
    
    ### 使用接口
    
    ```c
    int main() {
        // 步骤 4: 创建圆形对象并使用接口调用
        // 1. 分配内存存储Circle_Impl实例
        struct Circle_Impl *circle = (struct Circle_Impl*)malloc(sizeof(struct Circle_Impl));
        if (!circle) {
            // 处理内存分配失败
            return 1;
        }
    
        // 2. 初始化接口结构体和实现结构体的关系
        // 构建Geom_I结构体指针(即接口指针 pi)
        Geom_I *pi = &(circle->base); //注意这里将 Circle_Impl 的 base 成员与 Geom_I 结构体关联
    
        // 先初始化 Circle_Impl 部分
        circle->base.base = NULL;  // optional,可以根据需要设置
        circle->radius = 5.0;  // 操作私有数据
    
        // 3. 通过接口指针调用函数
        double a = pi->area(pi);   // 调用接口上的 area 函数
        double p = pi->perimeter(pi); // 调用接口上的 perimeter 函数
    
        printf("Circle Area: %f, Perimeter: %f
    ", a, p);
    
        // 步骤 5:查找、初始化和创建对象(如果使用工厂函数)
        // 假设有一个创建圆形对象的工厂函数
        // Geom_I *create_circle(double r) {
        //     // 分配内存
        //     // 构建接口并关联实现
        // }
    
        // 可以编写工厂函数创建不同的几何形状对象
    
        // 记住释放内存
        free(circle);
        return 0;
    }

    总结与强调:

    1. 接口定义: 使用 typedef struct ... 创建一个新的接口类型 Geom_I。这个类型本质上是函数指针的集合。
    2. 实现: 创建私有结构体来存放具体函数的实现地址和必要的内部数据。实例总数Geom_Impl是基础。圆形实例Circle_Impl继承了Geom_Impl
    3. 函数绑定: 将具体的函数实现(如 circle_area)与接口函数指针绑定。点击可在菜鸟教程查看C语言函数指针:https://www.runoob.com/cprogramming/c-function-pointers.html
    4. 对象创建与销毁: 提供工厂函数来创建接口指针,并在需要时提供销毁函数来释放相关的结构体和数据。
    5. 使用: 指向接口类型(Geom_I *)的指针提供了调用接口上函数的统一方式。
    6. 注意事项:
      • 类型安全: C语言是静态类型语言,这种模拟接口的方法实际上是不安全的。无法保证一个指向 Geom_I 类型指针的变量确实指向了一个实现了完整接口的对象,只能通过严格的编码规范和程序员自觉来保证。
      • 可见性: 实现结构体(如 Geom_Impl, Circle_Impl)应该定义为 static 或放在其他不可访问的文件中,以防止外部篡改。
      • 成员访问: 通过 self->area 访问函数指针,然后调用;通过 self->lpVtbl->area 的方式不是标准做法,实际上需要计算正确的偏移量来访问私有数据(更常见于 COM 等复杂系统)。
      • 封装: 真正的封装(如 CC语言的protected成员)很难实现,这里只能通过隐藏定义来达到一定程度的封装。

    这就是在 C语言 中模拟和使用接口的方法,虽然不如面向对象语言方便,但通过 typedef、结构体和函数指针组合,可以实现一定程度的接口抽象和程序的模块化、可扩展性。

    © 版权声明

    本文由盾科技原创,版权归 盾科技所有,未经允许禁止任何形式的转载。转载请联系candieraddenipc92@gmail.com