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

题图来自Unsplash,基于CC0协议
导读
C语言并没有像某些面向对象语言那样的显式接口(interface)关键字来定义抽象类或者接口。但是,我们可以通过 typedef 和函数指针来模拟接口的概念,实现类似的功能。这里就介绍如何在C语言中使用这种机制来创建和使用接口,就像在菜鸟教程中学习到的知识一样。
C语言接口模拟的基本概念
在 C语言 中接口通常被模拟为一种类型定义(typedef),其中包含一组函数指针。这组函数指针指向一组具有特定签名的函数地址。调用方通过持有这个接口类型的指针来调用接口上的函数,而不需要知道这些函数的具体实现。这样做的优点是实现了封装和解耦。
关键元素:
- 接口定义(TypeIDef): 用
typedef定义一个新的类型,这个类型实际上是一个包含函数指针的结构体。 - 实现结构体(ImplementationStruct): 定义一个结构体来存放接口函数的实现地址(函数指针)。这个结构体通常不对外公开其完整定义。
- 接口结构体(InterfaceStruct): 这个结构体通常被称为“接口”本身。它定义好接口需要提供的函数签名,并包含一个指向实现结构体的指针(或者本身就是实现结构体,但隐藏了内部实现方式和函数指针的具体数量)。
- 私有函数实现(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;
}
总结与强调:
- 接口定义: 使用
typedef struct ...创建一个新的接口类型Geom_I。这个类型本质上是函数指针的集合。 - 实现: 创建私有结构体来存放具体函数的实现地址和必要的内部数据。实例总数
Geom_Impl是基础。圆形实例Circle_Impl继承了Geom_Impl。 - 函数绑定: 将具体的函数实现(如
circle_area)与接口函数指针绑定。点击可在菜鸟教程查看C语言函数指针:https://www.runoob.com/cprogramming/c-function-pointers.html - 对象创建与销毁: 提供工厂函数来创建接口指针,并在需要时提供销毁函数来释放相关的结构体和数据。
- 使用: 指向接口类型(
Geom_I *)的指针提供了调用接口上函数的统一方式。 - 注意事项:
- 类型安全: C语言是静态类型语言,这种模拟接口的方法实际上是不安全的。无法保证一个指向
Geom_I类型指针的变量确实指向了一个实现了完整接口的对象,只能通过严格的编码规范和程序员自觉来保证。 - 可见性: 实现结构体(如
Geom_Impl,Circle_Impl)应该定义为 static 或放在其他不可访问的文件中,以防止外部篡改。 - 成员访问: 通过
self->area访问函数指针,然后调用;通过self->lpVtbl->area的方式不是标准做法,实际上需要计算正确的偏移量来访问私有数据(更常见于 COM 等复杂系统)。 - 封装: 真正的封装(如 CC语言的protected成员)很难实现,这里只能通过隐藏定义来达到一定程度的封装。
- 类型安全: C语言是静态类型语言,这种模拟接口的方法实际上是不安全的。无法保证一个指向
这就是在 C语言 中模拟和使用接口的方法,虽然不如面向对象语言方便,但通过 typedef、结构体和函数指针组合,可以实现一定程度的接口抽象和程序的模块化、可扩展性。
© 版权声明
本文由盾科技原创,版权归 盾科技所有,未经允许禁止任何形式的转载。转载请联系candieraddenipc92@gmail.com