C语言-数据结构-1-动态数组

本章为数据结构初学过程,通过实现动态数组,初步了解数据结构这一门课常见的代码

动态数组,就是能自动扩容的数组,当数组满的时候能够实现高效的扩容,节省系统空间

那么,在正式学习动态数组之前,我们需要先来了解以下四个常用的内存管理函数

(1)malloc()

函数原型:

cpp 复制代码
(类型说明符*) malloc(unsigned int size);

功能:在内存的动态存储区中分配一块长度为size字节的连续区域。

(2)calloc()

函数原型:

cpp 复制代码
(类型说明符*) calloc(n,size);

功能:在内存的动态存储区中分配n块长度为size字节的连续区域

(3)realloc

函数原型:

cpp 复制代码
(类型说明符*) realloc(void *p,int size);

功能:重新分配堆上的任意指针变量类型所指的空间,使其长度为size个字节,同时会复制原有内容到新分配的堆上存储空间。

注意,size可大可小(如果新的大小大于原内存大小,则新分配部分不会被初始化;如果新的大小小于原内存大小,可能会导致数据丢失)

(4)free()

函数原型:

cpp 复制代码
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)

cpp 复制代码
#include <stdio.h>   // 标准输入输出函数
#include <stdlib.h>  // 动态内存分配函数(malloc, calloc, realloc, free)

二、结构体定义

有了头文件,接下来我们定义一个结构体,这个结构体封装了三个元素(data指向动态分配的数组内存,size当前实际存储的元素个数,capacity数组的总容量)

通俗来讲,data就是一个指针,指向了我们要实际存储数据的位置,而size和capacity是用来辅助程序动态改变data的内存大小。

cpp 复制代码
typedef struct {
    int* data;      // 指向动态分配的数组内存
    int size;       // 当前实际存储的元素个数
    int capacity;   // 数组的总容量
} DynamicArray;

三、创建动态数组函数

有了这个前提,在后面的程序中,会经常用到DynamicArray这个结构体别名,用于创建新的结构体变量。

接下来我们来创建动态数组函数。其中,以DynamicArray为模板创建一个新的结构体变量arr,并使用malloc函数为其分配一个内存地址、使用calloc函数为arr的成员arr->data分配一个内存,initial_capacity在main函数中用来接收初始容量。

cpp 复制代码
DynamicArray* create_array(int initial_capacity) {
    DynamicArray* arr = (DynamicArray*)malloc(sizeof(DynamicArray));  // 分配结构体内存
    arr->data = (int*)calloc(initial_capacity, sizeof(int));          // 分配数组内存并初始化为0
    arr->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进行更新,即加一

cpp 复制代码
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
}

五、打印数组函数

cpp 复制代码
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");
}

六、销毁数组函数

cpp 复制代码
void destroy_array(DynamicArray* arr) {
    free(arr->data);  // 先释放数组内存
    free(arr);        // 再释放结构体内存
}

注意 :释放顺序很重要,如果先释放结构体,arr->data指针就会丢失。

七、主函数调试

cpp 复制代码
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, ..., 100
        print_array(arr);          // 每次添加后打印状态
    }
    
    destroy_array(arr);  // 释放内存
    return 0;
}

八、程序编译结果:

cpp 复制代码
动态数组演示
数组元素[大小=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 100

C:\Users\34088\source\repos\数据结构\x64\Debug\数据结构.exe (进程 31832)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用"工具"->"选项"->"调试"->"调试停止时自动关闭控制台"。
按任意键关闭此窗口. . .

完整代码如下

cpp 复制代码
#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);  // 初始容量为3

    printf("动态数组演示\n");

    // 添加元素测试自动扩容
    for (int i = 1; i <= 10; i++) {
        add_element(arr, i * 10);
        print_array(arr);
    }

    destroy_array(arr);
    return 0;
}
相关推荐
奔跑吧邓邓子2 小时前
【C语言实战(78)】C语言进阶:筑牢数据安全防线,密码学实战探秘
c语言·密码学·数据安全·开发实战
Herbert_hwt3 小时前
C语言循环结构完全指南:掌握for、while、do-while循环及实战应用
c语言
奔跑吧邓邓子3 小时前
【C语言实战(79)】深入C语言单元测试:基于CUnit框架的实战指南
c语言·单元测试·实战·cunit
ceclar1233 小时前
C++线程操作
c++
Miraitowa_cheems3 小时前
LeetCode算法日记 - Day 98: 分割回文串 II
数据结构·算法·leetcode·深度优先·动态规划
立志成为大牛的小牛3 小时前
数据结构——三十九、顺序查找(王道408)
数据结构·学习·程序人生·考研·算法
Shylock_Mister4 小时前
弱函数:嵌入式回调的最佳实践
c语言·单片机·嵌入式硬件·物联网
2301_807997384 小时前
代码随想录-day30
数据结构·c++·算法·leetcode
攒钱植发4 小时前
嵌入式Linux——“大扳手”与“小螺丝”:为什么不该用信号量(Semaphore)去模拟“完成量”(Completion)
linux·服务器·c语言