《抽象数据类型:把数据和对数据的操作绑在一起》
前言
C 语言是一门面向过程的语言,没有 class 类,但是我们可以利用头文件.h和源文件.c分离的写法,模拟面向对象里的封装思想,也就是抽象数据类型(ADT)。 所有数据结构统一使用这套规范编写,代码整洁、复用性强,也是大厂 C 语言项目的通用写法。
一、什么是抽象数据类型 ADT
抽象数据类型分为两部分:
- 数据本身:用结构体保存需要存储的所有成员;
- 操作集合:对数据的增删改查、初始化、销毁等函数。
核心思想:隐藏内部实现细节,对外只提供接口。使用者不需要知道结构体内部如何实现,只需要调用规定函数即可完成操作。
二、C 语言实现 ADT:头文件与源文件分离
1. 头文件 list.h(对外接口,给使用者看)
头文件只放结构体声明、函数声明,所有具体实现写在.c 文件中。
// list.h #ifndef LIST_H #define LIST_H // 结构体类型声明,对外隐藏具体成员 typedef struct List List; // 对外提供的接口函数 List* List_Init(); // 初始化 void List_Insert(List* list, int val); // 插入元素 void List_Delete(List* list, int val); // 删除元素 void List_Print(List* list); // 遍历打印 void List_Destroy(List* list); // 销毁内存 #endif2. 源文件 list.c(具体实现,内部隐藏)
// list.c #include "list.h" #include <stdio.h> #include <stdlib.h> // 完整结构体定义,只在当前文件可见 struct List { int* data; int size; int capacity; }; List* List_Init() { List* list = (List*)malloc(sizeof(List)); list->capacity = 4; list->size = 0; list->data = (int*)malloc(sizeof(int) * list->capacity); return list; } void List_Insert(List* list, int val) { list->data[list->size++] = val; } void List_Print(List* list) { for(int i = 0; i < list->size; i++) { printf("%d ", list->data[i]); } } void List_Destroy(List* list) { free(list->data); free(list); } // 删除函数省略实现 void List_Delete(List* list, int val) { }3. 主函数 main.c(使用者调用)
#include "list.h" int main() { // 用户完全不知道List结构体内部成员 List* L = List_Init(); List_Insert(L, 10); List_Insert(L, 20); List_Print(L); List_Destroy(L); return 0; }三、封装带来的好处
信息隐藏结构体具体成员只在.c 文件中可见,外部无法随意修改结构体内部变量,避免非法篡改数据,保证数据安全。
统一编码规范之后写链表、栈、队列、树全部沿用这套格式: 结构体 typedef、Init 初始化接口、增删查改接口、Destroy 销毁接口,代码风格高度统一。
便于维护和迭代后续优化底层实现,比如优化扩容逻辑、优化查找算法,只需要修改 list.c 文件,外部调用代码一行不用改动。
方便打包复用编译成静态库,其他人只需要拿到.h 头文件和库文件,就能直接使用数据结构,不用关心底层代码。
四、总结
抽象数据类型就是 C 语言里的 “面向对象封装”。 结构体存放属性,一系列函数作为方法,头文件暴露接口,源文件隐藏实现。 这是学习所有数据结构之前必须掌握的编码规范,熟练之后,后续手写任意数据结构都会顺手很多。