#include
#include
#include
// 动态数组结构体
typedef struct _stu_dynamicArray
{
void** data; // 堆区真实数组指针(由于不知道动态数组中要存储何种类型数据,因此用万能指针void*来代表所有数据类型包括自定义类型来作为数组中的元素。而外面就得用二级指针来指向一级指针了)
int capacity; // 数组最大容量
int size; // 数组元素大小
} stu_dynamicArray;
// 动态数组初始化
stu_dynamicArray* dynamicArrayInit(int capacity)
{
if (capacity < 1) {return NULL;}
// 在堆区开辟动态数组结构
stu_dynamicArray* pDA = (stu_dynamicArray*)malloc(sizeof(stu_dynamicArray));
if (pDA == NULL) { return NULL; } // 开辟失败返回空
// 设置初始大小
pDA->capacity = capacity;
pDA->size = 0;
// 在堆区开辟真实数组指针
pDA->data = (void*)malloc(sizeof(void*) * capacity);
if (pDA->data == NULL) // 开辟失败返回空
{
free(pDA);
return NULL;
}
return pDA;
}
// 动态数组指定位置插入
void dynamicArrayInsert(stu_dynamicArray* da, int pos, void* val)
{
if (da == NULL) { return; }
if (val == NULL) { return; }
if (pos < 0 || pos > da->size) { pos = da->size; } // 位置不正确则默认尾插
// 判断数组元素是否已经达到最大容量
if (da->size >= da->capacity)
{
// 在堆区重新开辟新空间
int newCapacity = da->capacity * 2;
void** newData = (void*)malloc(sizeof(void*) * newCapacity);
if (newData == NULL) { return; }
// 将原数据拷贝到新空间
memcpy(newData, da->data, sizeof(void*) * da->capacity);
// 释放原空间
free(da->data);
// 更改指针指向
da->data = newData;
// 更新容量
da->capacity = newCapacity;
}
// 开始插入
for (int i = da->size - 1; i >= pos; i--) // 从后往前移
{
da->data[i + 1] = da->data[i];
}
da->data[pos] = val;
// 更新元素大小
da->size++;
}
// 动态数组尾插法
void dynamicArrayPushBack(stu_dynamicArray* da, void* val)
{
dynamicArrayInsert(da, da->size, val);
}
// 动态数组指定位置删除
void dynamicArrayErase(stu_dynamicArray* da, int pos)
{
if (da == NULL) { return; }
if (pos < 0 || pos > da->size - 1) { return; } // 位置不正确则返回
// 开始删除
for (int i = pos; i < da->size - 1; i++) // da->size - 1这个减1很重要啊
{
da->data[i] = da->data[i + 1];
}
// 更新元素大小
da->size--;
}
// 动态数组尾删法
void dynamicArrayPopBack(stu_dynamicArray* da)
{
if (da == NULL) { return; }
if (da->size > 0)
{
da->size--;
}
}
// 动态数组销毁
void dynamicArrayDestroy(stu_dynamicArray* da)
{
if (da == NULL) { return; }
if (da->data != NULL)
{
free(da->data);
da->data = NULL;
}
free(da);
da = NULL;
}
// 动态数组遍历(利用回调函数)
void dynamicArrayForEach(stu_dynamicArray* da,void(*myForEach)(void*))
{
if (da == NULL) { return; }
if (myForEach == NULL) { return; }
for (int i = 0; i < da->size; i++)
{
myForEach(da->data[i]);
}
}
// 测试用结构体
struct _stu_person
{
char name[31];
int age;
};
// 测试用回调函数
void myPrintPerson(void* val)
{
struct _stu_person* p = val;
printf("姓名:%s 年龄:%d\n",p->name,p->age);
}
// 测试动态数组
void testDynamicArray()
{
// 创建动态数组
stu_dynamicArray* arr = dynamicArrayInit(4);
// 测试数据
struct _stu_person p1 = { "刘备",39 };
struct _stu_person p2 = { "关羽",34 };
struct _stu_person p3 = { "张飞",32 };
struct _stu_person p4 = { "赵云",28 };
struct _stu_person p5 = { "吕布",30 };
// 开始插入
dynamicArrayPushBack(arr ,&p1);
dynamicArrayInsert(arr, 10, &p2);
dynamicArrayInsert(arr, 1, &p3);
// 数据遍历
printf("=====当前容量:%d,元素个数:%d=====\n", arr->capacity,arr->size);
dynamicArrayForEach(arr, myPrintPerson);
// 扩容测试
dynamicArrayPushBack(arr, &p4);
dynamicArrayInsert(arr, 0, &p5);
// 重新遍历(不用遍历函数)
printf("\n=====扩容后容量:%d,元素个数:%d=====\n", arr->capacity, arr->size);
for (int i = 0; i < arr->size; i++)
{
struct _stu_person* p = arr->data[i]; // arr->data是2级指针,而arr->data[i]是1级指针
printf("姓名:%s 年龄:%d\n", p->name, p->age);
}
// 删除数据
dynamicArrayErase(arr,1);
printf("\n=====删除第一个位置数据后的容量:%d,元素个数:%d=====\n", arr->capacity, arr->size);
dynamicArrayForEach(arr, myPrintPerson);
dynamicArrayPopBack(arr);
printf("\n=====删除最后位置数据后的容量:%d,元素个数:%d=====\n", arr->capacity, arr->size);
dynamicArrayForEach(arr, myPrintPerson);
// 销毁数组
dynamicArrayDestroy(arr);
printf("\n=====数组已经销毁=====\n");
}
// 主函数
int main()
{
testDynamicArray();
return 0;
}
上面的动态数组已经能满足用户的大部分要求了,但这里有一个很大的问题没解决,就是这个动态数组结构赤果果的呈现在用户面前,那么用户就可以任意修改其中的数据了,比如说数组元素大小,数组最大容量等。这是让人无法容忍的,必须封装,必须保持神秘感,必须不让用户接触到这些东西。那么封装的关键点在哪呢?毋容置疑,当然是【stu_dynamicArray* dynamicArrayInit(int capacity)】这个用户第一个接触到的动态数组初始化函数了。在该函数中返回的直接就是stu_dynamicArray这个结构体,这绝对不行,改为返回void*这个万能指针,然后我们在具体函数中再将其偷偷强制转换为stu_dynamicArray这个结构体,不要让用户知道不就行了。
#include
#include
#include
// 动态数组结构体
typedef struct _stu_dynamicArray
{
void** data; // 堆区真实数组指针(由于不知道动态数组中要存储何种类型数据,因此用万能指针void*来代表所有数据类型包括自定义类型来作为数组中的元素。而外面就得用二级指针来指向一级指针了)
int capacity; // 数组最大容量
int size; // 数组元素大小
} stu_dynamicArray;
// 用万能指针来代替动态数组结构体,这是封装的关键
typedef void* dynamicArray;
// 动态数组初始化
dynamicArray dynamicArrayInit(int capacity)
{
if (capacity < 1) {return NULL;}
// 在堆区开辟动态数组结构
stu_dynamicArray* pDA = (stu_dynamicArray*)malloc(sizeof(stu_dynamicArray));
if (pDA == NULL) { return NULL; } // 开辟失败返回空
// 设置初始大小
pDA->capacity = capacity;
pDA->size = 0;
// 在堆区开辟真实数组指针
pDA->data = (void*)malloc(sizeof(void*) * capacity);
if (pDA->data == NULL) // 开辟失败返回空
{
free(pDA);
pDA = NULL;
return NULL;
}
return pDA;
}
// 动态数组指定位置插入
void dynamicArrayInsert(dynamicArray arr, int pos, void* val)
{
if (arr == NULL) { return; }
if (val == NULL) { return; }
stu_dynamicArray* da = (stu_dynamicArray*)arr; // 强制转换
if (pos < 0 || pos > da->size) { pos = da->size; } // 位置不正确则默认尾插
// 判断数组元素是否已经达到最大容量
if (da->size >= da->capacity)
{
// 在堆区重新开辟新空间
int newCapacity = da->capacity * 2;
void** newData = (void*)malloc(sizeof(void*) * newCapacity);
if (newData == NULL) { return; }
// 将原数据拷贝到新空间
memcpy(newData, da->data, sizeof(void*) * da->capacity);
// 释放原空间
free(da->data);
// 更改指针指向
da->data = newData;
// 更新容量
da->capacity = newCapacity;
}
// 开始插入
for (int i = da->size - 1; i >= pos; i--) // 从后往前移
{
da->data[i + 1] = da->data[i];
}
da->data[pos] = val;
// 更新元素大小
da->size++;
}
// 动态数组尾插法
void dynamicArrayPushBack(dynamicArray arr, void* val)
{
if (arr == NULL) { return; }
if (val == NULL) { return; }
stu_dynamicArray* da = (stu_dynamicArray*)arr; // 强制转换
dynamicArrayInsert(arr, da->size, val);
}
// 动态数组指定位置删除
void dynamicArrayErase(dynamicArray arr, int pos)
{
if (arr == NULL) { return; }
stu_dynamicArray* da = (stu_dynamicArray*)arr; // 强制转换
if (pos < 0 || pos > da->size - 1) { return; } // 位置不正确则返回
// 开始删除
for (int i = pos; i < da->size - 1; i++) // da->size - 1这个减1很重要啊
{
da->data[i] = da->data[i + 1];
}
// 更新元素大小
da->size--;
}
// 动态数组尾删法
void dynamicArrayPopBack(dynamicArray arr)
{
if (arr == NULL) { return; }
stu_dynamicArray* da = (stu_dynamicArray*)arr; // 强制转换
if (da->size > 0)
{
da->size--;
}
}
// 动态数组销毁
void dynamicArrayDestroy(dynamicArray arr)
{
if (arr == NULL) { return; }
stu_dynamicArray* da = (stu_dynamicArray*)arr; // 强制转换
if (da->data != NULL)
{
free(da->data);
da->data = NULL;
}
free(da);
da = NULL;
}
// 得到动态数组最大容量
int dynamicArrayCapacity(dynamicArray arr)
{
if (arr == NULL) { return -1; }
stu_dynamicArray* da = (stu_dynamicArray*)arr; // 强制转换
return da->capacity;
}
// 得到动态数组中元素大小
int dynamicArraySize(dynamicArray arr)
{
if (arr == NULL) { return -1; }
stu_dynamicArray* da = (stu_dynamicArray*)arr; // 强制转换
return da->size;
}
// 动态数组遍历(利用回调函数)
void dynamicArrayForEach(dynamicArray arr,int(*myForEach)(void*))
{
if (arr == NULL) { return; }
stu_dynamicArray* da = (stu_dynamicArray*)arr; // 强制转换
if (myForEach == NULL) { return; }
for (int i = 0; i < da->size; i++)
{
if (myForEach(da->data[i]) == -1) { break; } // 根据返回值判断是否中途退出遍历
}
}
// 测试用结构体
struct _stu_person
{
char name[31];
int age;
};
// 测试用回调函数(返回-1则退出遍历)
int myPrintPerson(void* val)
{
struct _stu_person* p = val;
printf("姓名:%s 年龄:%d\n",p->name,p->age);
return 0;
}
// 测试动态数组
void testDynamicArray()
{
// 创建动态数组
dynamicArray arr = dynamicArrayInit(4);
// 测试数据
struct _stu_person p1 = { "刘备",39 };
struct _stu_person p2 = { "关羽",34 };
struct _stu_person p3 = { "张飞",32 };
struct _stu_person p4 = { "赵云",28 };
struct _stu_person p5 = { "吕布",30 };
// 开始插入
dynamicArrayPushBack(arr ,&p1);
dynamicArrayInsert(arr, 10, &p2);
dynamicArrayInsert(arr, 1, &p3);
// 数据遍历
printf("=====当前容量:%d,元素个数:%d=====\n", dynamicArrayCapacity(arr), dynamicArraySize(arr));
dynamicArrayForEach(arr, myPrintPerson);
// 扩容测试
dynamicArrayPushBack(arr, &p4);
dynamicArrayInsert(arr, 0, &p5);
// 重新遍历
printf("\n=====扩容后容量:%d,元素个数:%d=====\n", dynamicArrayCapacity(arr), dynamicArraySize(arr));
dynamicArrayForEach(arr, myPrintPerson);
// 删除数据
dynamicArrayErase(arr,1);
printf("\n=====删除第一个位置数据后的容量:%d,元素个数:%d=====\n", dynamicArrayCapacity(arr), dynamicArraySize(arr));
dynamicArrayForEach(arr, myPrintPerson);
dynamicArrayPopBack(arr);
printf("\n=====删除最后位置数据后的容量:%d,元素个数:%d=====\n", dynamicArrayCapacity(arr), dynamicArraySize(arr));
dynamicArrayForEach(arr, myPrintPerson);
// 销毁数组
dynamicArrayDestroy(arr);
printf("\n=====数组已经销毁=====\n");
}
// 主函数
int main()
{
testDynamicArray();
return 0;
}