目录

动态数组

目录

C语言中数组大小是固定的,如果我们一开始并不知道数组的大小,用起来就很麻烦

这时候,就可以使用动态数组,如何实现动态数组呢?

在C语言中,可以使用malloc函数来动态分配内存,然后使用realloc函数来调整内存的大小。

realloc 函数会重新分配内存,并将原内存中的数据复制到新内存中。如果新内存的大小大于原内存的大小,则新内存中的部分内存将是未定义的。

动态数组(vector)结构定义

1
2
3
4
5
typedef struct {
    int len; // 数组当前大小
    int cap; // 数组容量
    int* data; // 数组数据
} Vec;

其中len为数组的大小,在数组中没有数据时为0,cap为数组的容量,是在数组创建时定义的,如果数组大小超过了设置的容量,就需要使用realloc进行扩容,data为数组的数据指针。

但是,这样定义的数组,只能存储int类型的数据,如何设计动态数组,使其能够存储任意类型的数据呢?

我们可以使用void*指针来存储任意类型的数据,这样就可以实现动态数组了。

在看了几个开源仓库的源码后,我发现可以使用宏定义来实现动态数组的多态

1
2
3
4
5
6
#define Vec(T) struct { int len; int cap; T* data; }

// 使用示例
Vec(int) v = { 0, 0, NULL };
Vec(double) v = { 0, 0, NULL };
Vec(char) v = { 0, 0, NULL };

这样,我们就可以创建不同类型的动态数组了。

但是又有一个,问题,由于事先不知道数组元素是什么类型,如何实现添加或删除元素呢?

还是要用宏定义实现,使用sizeof编译时操作符获取元素的大小,使用mem函数实现操作

1
2
#define vec_unpack(v)                     \
    (char **)&(v)->data, &(v)->len, &(v)->cap, sizeof(*(v)->data)

使用该宏定义,可以在编译时期获取数组指针,数组大小,数组容量,数组中每个元素的大小,然后就可以编写函数实现动态数组的功能了

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
static void vec_append(char **data, int *length, int *capacity, int memsz) {
    if (*length + 1 > *capacity) {
        *capacity = (int)(*capacity * VEC_EXPAND_FACTOR + 1);
        *data = (char *)realloc(*data, *capacity * memsz);
    }
}

动态数组的完整实现

```c
#ifndef __VEC_H__
#define __VEC_H__

#include <stdlib.h>
#include <string.h>

// 扩容因子
#define VEC_EXPAND_FACTOR 1.5
#define VEC_EXTEND_FACTOR 1.25

/**
 * @brief 声明一个动态数组
 * @param T 数组中每个元素的数据类型
 */
#define Vec(T)                            \
    struct {                              \
        T *data;                          \
        int len;                          \
        int cap;                          \
    }

/**
 * @brief 数组解包
 * @param v 数组指针
 */
#define vec_unpack(v)                     \
    (char **)&(v)->data, &(v)->len, &(v)->cap, sizeof(*(v)->data)

/**
 * @brief 动态数组扩容
 * @param data 数组指针
 * @param length 数组长度
 * @param capacity 数组容量
 * @param memsz 数组中每个元素的大小
 */
static void vec_append(char **data, int *length, int *capacity, int memsz) {
    if (*length + 1 > *capacity) {
        *capacity = (int)(*capacity * VEC_EXPAND_FACTOR + 1);
        *data = (char *)realloc(*data, *capacity * memsz);
    }
}

static void vec_extend(char **data, int *length, int *capacity, int memsz,
                       char **values, int count) {
    if (*length + count > *capacity) {
        *capacity = (int)((*length + count) * VEC_EXTEND_FACTOR + 1);
        *data = (char *)realloc(*data, *capacity * memsz);
    }
    memcpy(*data + *length * memsz, *values, count * memsz);
    *length += count;
}

/**
 * @brief 删除元素
 * @param data 数组指针
 * @param length 数组长度
 * @param capacity 数组容量
 * @param memsz 数组中每个元素的大小
 * @param start 删除的起始位置
 * @param count 删除的元素个数
 */
static inline void vec_remove(char **data, int *length, int *capacity,
                              int memsz, int start, int count) {
    (void)capacity;
    memmove(*data + start * memsz, *data + (start + count) * memsz,
            (*length - start - count) * memsz);
    *length -= count;
}

/**
 * @brief 获取切片
 * @param data 数组指针
 * @param length 数组长度
 * @param capacity 数组容量
 * @param memsz 数组中每个元素的大小
 * @param start 切片的起始位置
 * @param count 切片的元素个数
 */
static inline void *vec_splice(char **data, int *length, int *capacity,
                               int memsz, int start, int count) {
    (void)capacity;
    void *ptr = malloc(count * memsz);
    memcpy(ptr, *data + start * memsz, count * memsz);
    return ptr;
}

/**
 * @brief 初始化动态数组
 * @param v 数组指针
 * @param __len 初始数组长度
 * @param __cap 初始数组容量
 */
#define vec_init(v, __len, __cap)                                              \
    do {                                                                       \
        (v)->data = malloc((__cap) * sizeof(typeof(*(v)->data)));              \
        (v)->len = (__len);                                                    \
        (v)->cap = (__cap);                                                    \
    } while (0)

/**
 * @brief 释放动态数组
 */
#define vec_free(v) free((v)->data)

#define vec_clear(v) ((v)->len = 0)

#define vec_append(v, val)                                                     \
    (vec_append(vec_unpack(v)), (v)->data[(v)->len++] = (val))

/**
 * @brief 返回数组切片
 * @param dest 目标数组指针
 * @param src 源数组指针
 * @param start 切片的起始位置, 包含, 负数表示从后往前数
 * @param end 切片的中止位置,不包含, 负数表示从后往前数
 */
#define vec_splice(dest, src, start, end)                                      \
    do {                                                                       \
        int l = start, r = end;                                                \
        if (start < 0) {                                                       \
            l = (src)->len + start;                                            \
        }                                                                      \
        if (end < 0) {                                                         \
            r = (src)->len + end;                                              \
        }                                                                      \
        (dest)->len = (r) - (l);                                               \
        (dest)->cap = (dest)->len;                                             \
        (dest)->data = vec_splice(vec_unpack(src), l, r - l);                  \
    } while (0)

/**
 * @brief 删除数组元素
 * @param v 数组指针
 * @param start 删除的起始位置
 * @param count 删除的元素个数
 */
#define vec_remove(v, start, count) vec_remove(vec_unpack(v), start, count)

/**
 * @brief 扩展数组
 */
#define vec_extend(v, buf, length) vec_extend(vec_unpack(v), buf, length)

#define vec_pop(v)                                                             \
    do {                                                                       \
        if ((v)->len > 0) {                                                    \
            (v)->len--;                                                        \
        }                                                                      \
    } while (0)
#endif