《复盘:用刚学的结构体和指针,重新 “封装” 一个动态数组》

📅 2026/7/6 3:11:30
《复盘:用刚学的结构体和指针,重新 “封装” 一个动态数组》
前言1. 先说原生数组的痛点C 语言普通数组int arr[5];缺点所有人都深有体会大小固定写死 5 个空间存满之后不能扩容空间开辟太大浪费内存开辟太小不够用插入、删除中间元素需要大量挪动元素效率低没有配套功能求长度、扩容、清空都要自己重复写代码。所以我们目标亲手写一个可以自动扩容的动态数组这是从基础语法跨入数据结构真正的分水岭。 能独立写出动态数组说明你真正吃透了结构体、指针、malloc、realloc、内存管理、封装思想。2. 动态数组整体设计我们用结构体打包所有数据typedef struct DynamicArray { int* data; // 指向堆区的数组空间 int size; // 当前实际存放元素个数 int capacity; // 总容量最多能存多少元素 } DArray;三个成员各司其职 data 指向堆上开辟的连续内存size 记录当前元素数量capacity 记录最大容量。核心规则自动扩容逻辑初始化时默认开辟 4 个容量每次添加元素前判断如果 size capacity代表空间已满扩容策略容量扩大为原来 2 倍使用 realloc 重新分配内存。3. 完整功能清单创建动态数组初始化malloc 分配结构体和初始数组空间尾插元素最常用操作判断是否需要扩容指定下标插入元素元素后移指定下标删除元素元素前移根据下标获取元素遍历打印所有元素获取有效元素个数清空元素、销毁数组free 释放所有堆内存防止内存泄漏4. 关键难点讲解难点 1realloc 扩容realloc 会重新开辟一块更大内存把旧数据拷贝过去旧内存自动回收。 注意判断 realloc 返回值不能为 NULL防止内存开辟失败。if (da-size da-capacity) { int newCap da-capacity * 2; int* newData realloc(da-data, newCap * sizeof(int)); if (newData NULL) { printf(内存不足扩容失败\n); return; } da-data newData; da-capacity newCap; }难点 2边界检查插入、删除、取值之前必须判断下标是否合法。 比如下标小于 0或者大于等于当前有效长度直接报错杜绝数组越界。难点 3内存释放动态数组全部在堆上开辟使用完毕必须先释放 data 数组再释放结构体本身否则造成内存泄漏这是 C 语言重中之重。5. 为什么说这是分水岭告别 “零散代码” 思维开始用结构体批量管理数据熟练掌握堆内存的申请、扩容、释放彻底分清栈内存和堆内存区别学会边界判断、异常处理代码不再只能处理完美数据这套模式可以无缝迁移顺序表写完链表、栈、队列全部都是一模一样的封装套路6. 完整使用示例int main() { DArray* arr da_create(); da_add(arr, 10); da_add(arr, 20); da_add(arr, 30); da_add(arr, 40); // 此时达到初始容量再次添加自动扩容 da_add(arr, 50); da_print(arr); da_delete_index(arr, 1); da_print(arr); // 程序结束销毁 da_destroy(arr); return 0; }结尾总结手写动态数组看起来只是一个简单练习实际上是完整训练 结构体封装 指针操作 堆内存管理 边界校验 模块化接口设计。 搞定这个之后线性表相关的数据结构对你来说就没有任何难度了。