C语言-数据结构-1-动态数组
本章为数据结构初学过程,通过实现动态数组,初步了解数据结构这一门课常见的代码
动态数组,就是能自动扩容的数组,当数组满的时候能够实现高效的扩容,节省系统空间
那么,在正式学习动态数组之前,我们需要先来了解以下四个常用的内存管理函数
(1)malloc()
函数原型:
(类型说明符*) malloc(unsigned int size);
功能:在内存的动态存储区中分配一块长度为size字节的连续区域。
(2)calloc()
函数原型:
(类型说明符*) calloc(n,size);
功能:在内存的动态存储区中分配n块长度为size字节的连续区域
(3)realloc
函数原型:
(类型说明符*) realloc(void *p,int size);
功能:重新分配堆上的任意指针变量类型所指的空间,使其长度为size个字节,同时会复制原有内容到新分配的堆上存储空间。
注意,size可大可小(如果新的大小大于原内存大小,则新分配部分不会被初始化;如果新的大小小于原内存大小,可能会导致数据丢失)
(4)free()
函数原型:
void free(void* p);
功能:释放void*p所指的内存空间
以上四个常用的内存管理函数介绍来源于https://blog.csdn.net/jianbai_/article/details/109728592?fromshare=blogdetail&sharetype=blogdetail&sharerId=109728592&sharerefer=PC&sharesource=Mondie4387&sharefrom=from_link
那么,初略了解完四个常用的内存管理函数malloc、calloc、realloc、free函数之后,让我们正式进入数据结构&动态数组的学习
一、头文件引入
stdio.h文件包含了标准输入输出函数,这个大家都非常熟悉
stdlib.h文件包含了四个常用的动态内存分配函数(内存管理函数)(malloc, calloc, realloc, free)
#include <stdio.h> // 标准输入输出函数
#include <stdlib.h> // 动态内存分配函数(malloc, calloc, realloc, free)
二、结构体定义
有了头文件,接下来我们定义一个结构体,这个结构体封装了三个元素(data指向动态分配的数组内存,size当前实际存储的元素个数,capacity数组的总容量)
通俗来讲,data就是一个指针,指向了我们要实际存储数据的位置,而size和capacity是用来辅助程序动态改变data的内存大小。
typedef struct {int* data; // 指向动态分配的数组内存int size; // 当前实际存储的元素个数int capacity; // 数组的总容量
} DynamicArray;
三、创建动态数组函数
有了这个前提,在后面的程序中,会经常用到DynamicArray这个结构体别名,用于创建新的结构体变量。
接下来我们来创建动态数组函数。其中,以DynamicArray为模板创建一个新的结构体变量arr,并使用malloc函数为其分配一个内存地址、使用calloc函数为arr的成员arr->data分配一个内存,initial_capacity在main函数中用来接收初始容量。
DynamicArray* create_array(int initial_capacity) {DynamicArray* arr = (DynamicArray*)malloc(sizeof(DynamicArray)); // 分配结构体内存arr->data = (int*)calloc(initial_capacity, sizeof(int)); // 分配数组内存并初始化为0arr->size = 0; // 初始时没有元素arr->capacity = initial_capacity; // 设置初始容量return arr;
}
完成了动态数组函数的初步创建,接下来让我们进行下一步
四、添加元素函数
需要实现的功能有:
(1)检查data是否需要扩容,该步骤可通过比较size当前元素数量和capacity总容量来进行判断。
需要扩容的话,我们就要创建new_capacity和*new_data这两个变量来临时存储改变的总容量和新地址,因为扩容的时候不能破坏原有的数据,所以使用了realloc函数来进行扩容。
(2)需要检查内存是否分配成功,通过判断new_data是否为NULL来进行检查,若new_data为空的话,表示分配失败,需要结束整个程序,若成功,在后续就可以把new_data和new_capacity赋值给arr->data和arr->capacity了。
(3)无论是否需要扩容,在本函数的最后一步,需要对arr->data[arr->size]进行赋值,因为本函数的作用就是在data中增加新的数据。当数据增加完之后,要对size进行更新,即加一
void add_element(DynamicArray* arr, int value) {if (arr->size >= arr->capacity) { // 检查是否需要扩容int new_capacity = arr->capacity * 2; // 容量翻倍int* new_data = (int*)realloc(arr->data, new_capacity * sizeof(int));if (new_data == NULL) { // 检查内存分配是否成功printf("内存分配失败!\n");return;}arr->data = new_data; // 更新数据指针arr->capacity = new_capacity; // 更新容量printf("数组已扩容至 %d\n", new_capacity);}arr->data[arr->size] = value; // 在末尾添加新元素arr->size++; // 元素计数加1
}
五、打印数组函数
void print_array(const DynamicArray* arr) { // const保护参数不被修改printf("数组元素[大小=%d, 容量=%d]: ", arr->size, arr->capacity);for (int i = 0; i < arr->size; i++) {printf("%d ", arr->data[i]); // 遍历打印所有元素}printf("\n");
}
六、销毁数组函数
void destroy_array(DynamicArray* arr) {free(arr->data); // 先释放数组内存free(arr); // 再释放结构体内存
}
注意:释放顺序很重要,如果先释放结构体,arr->data指针就会丢失。
七、主函数调试
int main() {DynamicArray* arr = create_array(3); // 创建初始容量为3的数组printf("动态数组演示\n");// 添加10个元素测试自动扩容for (int i = 1; i <= 10; i++) {add_element(arr, i * 10); // 添加元素10, 20, 30, ..., 100print_array(arr); // 每次添加后打印状态}destroy_array(arr); // 释放内存return 0;
}
八、程序编译结果:
动态数组演示
数组元素[大小=1, 容量=3]: 10
数组元素[大小=2, 容量=3]: 10 20
数组元素[大小=3, 容量=3]: 10 20 30
数组已扩容至 6
数组元素[大小=4, 容量=6]: 10 20 30 40
数组元素[大小=5, 容量=6]: 10 20 30 40 50
数组元素[大小=6, 容量=6]: 10 20 30 40 50 60
数组已扩容至 12
数组元素[大小=7, 容量=12]: 10 20 30 40 50 60 70
数组元素[大小=8, 容量=12]: 10 20 30 40 50 60 70 80
数组元素[大小=9, 容量=12]: 10 20 30 40 50 60 70 80 90
数组元素[大小=10, 容量=12]: 10 20 30 40 50 60 70 80 90 100C:\Users\34088\source\repos\数据结构\x64\Debug\数据结构.exe (进程 31832)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
完整代码如下
#include <stdio.h>
#include <stdlib.h>typedef struct {int* data;//指向动态分配数组的指针,本处指针的用处就是将数组存储区域独立出结构体,因此本处位置只存储了一串定长的地址int size;//当前数组中实际存储的元素个数int capacity;//数组的总容量(最多能存储的元素个数)
} DynamicArray;// 创建动态数组//本代码中,调用该函数,先分配3个int大小的地址
DynamicArray* create_array(int initial_capacity) {DynamicArray* arr = (DynamicArray*)malloc(sizeof(DynamicArray));//先为结构体分配地址arr->data = (int*)calloc(initial_capacity, sizeof(int));//后为结构体成员arr->data分配地址arr->size = 0;arr->capacity = initial_capacity;return arr;
}// 添加元素
void add_element(DynamicArray* arr, int value) {if (arr->size >= arr->capacity) {//当实际个数大于总容量时,要进行扩容int new_capacity = arr->capacity * 2;int* new_data = (int*)realloc(arr->data, new_capacity * sizeof(int));if (new_data == NULL) {printf("内存分配失败!\n");return;}arr->data = new_data;arr->capacity = new_capacity;printf("数组已扩容至 %d\n", new_capacity);}arr->data[arr->size] = value;//在数组末尾添加新元素arr->size++;// 增加元素计数
}// 打印数组
void print_array(const DynamicArray* arr) {//其中const的用处是保护arr使其无法被修改printf("数组元素[大小=%d, 容量=%d]: ", arr->size, arr->capacity);for (int i = 0; i < arr->size; i++) {printf("%d ", arr->data[i]);}printf("\n");
}// 销毁数组
void destroy_array(DynamicArray* arr) {//内存释放顺序:必须先释放data指向的内存,再释放结构体内存free(arr->data);free(arr);
}int main() {DynamicArray* arr = create_array(3); // 初始容量为3printf("动态数组演示\n");// 添加元素测试自动扩容for (int i = 1; i <= 10; i++) {add_element(arr, i * 10);print_array(arr);}destroy_array(arr);return 0;
}
