自制可变数组
具有的功能:
- 初始化
- 判断是否为空
- 判断是否满
- 展示数组
- 在末尾追加值(若数组已满可自动增长)
- 在中间插入值
- 删除指定位置的值
- 倒置数组
- 整理数组
搭建(diy)过程
- 自定义一个结构类型 Arr,包含一个指针 pBase、数组总长度 len、数组有效长度 cnt;
~~~ C
typedef struct {
int *pBase;
int len;
int cnt;
} Arr;
int main(void)
{
Arr arr;
return 0;
}
- 指针 pBase 就类似普通数组中数组变量名的作用,对其进行操作就可以获得数组中的任一元素
- 有了结构类型 Arr 后,若只是单纯命名一个 Arr 变量,这个变量是不包含任何有效信息的,这时其中的pBase 没有指向任何有效区域,而且数组总长度 len 也没有给出,所以需要对其进行初始化,编写初始化函数 init_arr();
void init_arr(Arr * pArr, int length);
int main(void)
{
Arr arr;
int length = 2;
init_arr(&arr, length);
return 0;
}
void init_arr(Arr * pArr, int length)
{
pArr->pBase = (int *)malloc(sizeof(int) * length);
if ( pArr->pBase == NULL )
{
printf("动态内存分配失败!!!\n");
exit(-1);
}
else
{
pArr->len = length;
pArr->cnt = 0;
}
return;
}
- 有了初始化的功能后,如果要查看该数组,采用循环的方式就很不方便了,此时可以定义一个 show_arr() 函数,使其直接能够展示给用户看,需要注意的是,如果数组为空,需要告诉用户,这时可以用一个返回bool 值的 is_empty() 函数来完成判断;
```c
bool is_empty(Arr * pArr);
void show_arr(Arr * pArr);
int main(void)
{
Arr arr;
int length = 2;
init_arr(&arr, length);
show_arr(&arr);
return 0;
}
bool is_empty(Arr * pArr)
{
if ( pArr->cnt == 0 )
{
return true;
}
else
{
return false;
}
}
void show_arr(Arr * pArr)
{
if ( is_empty(pArr) )
{
printf("还没有向数组添加任何元素!!!\n");
}
else
{
for ( int i=0;i<pArr->cnt;i++ )
{
printf("%d ", pArr->pBase[i]);
}
printf("\n");
}
}
4. 到现在为止,还没有办法向数组添加任何元素,先定义一个简单的 append_arr() 函数,其作用是在数组的末尾添加一个元素,这时需要注意,如果数组的空间已满,就无法简单的在最后插入一个元素了,而需要增大数组空间,这里使用的是申请另一块大内存的方式来实现”自动扩容“功能,还需要一个判断数组是否已满的函数;
```c
bool is_full(Arr * pArr);
void append_arr(Arr * pArr, int val);
const int unit = 4; //定义一个常量 unit,代表数组空间不够时增长的大小
int main(void)
{
Arr arr;
int length = 2;
init_arr(&arr, length);
append_arr(&arr, 2);
append_arr(&arr, 7);
show_arr(&arr);
return 0;
}
bool is_full(Arr * pArr)
{
if ( pArr->cnt < pArr->len )
// 将当前数组有效长度与总长度进行比较,若 cnt>=len 已满
{
return false;
}
else
{
return true;
}
}
void append_arr(Arr * pArr, int val)
{
// val 代表添加元素的值
if ( is_full(pArr) )
{
int * p;
int length = pArr->len + unit;
p = (int *)malloc(sizeof(int) * length);
// 申请另一块比原数组空间大 1 个的 unit 的数组
if ( p == NULL ) // 若 malloc 函数返回 NULL 值,
// 说明内存不足,此时终止程序
{
printf("动态内存分配失败!!!\n");
exit(-1); //终止整个程序
}
else
{
for ( int i=0;i<pArr->cnt;i++ )
{
p[i] = pArr->pBase[i];
// 将原数组空间中的值赋值给新数组空间
}
free(pArr->pBase);
// 完成赋值后,将原数组空间释放,避免占用系统空间
pArr->pBase = p;
// 将新申请到的数组空间赋值给原数组的指针,即使得原数组的指针指向新的一片大空间
pArr->pBase[pArr->cnt++] = val;
// 将新添加的元素放到数组最后位置,并将有效长度 cnt++
pArr->len = length;
// 更新数组长度
}
}
else
{
pArr->pBase[pArr->cnt++] = val;
// 将新添加的元素放到数组最后位置,并将有效长度 cnt++
}
return;
}
- 有了在末尾添加元素的功能后,感觉还需要一个在数组中间插入元素的功能,不然感觉缺了一块,于是来定义一个 insert_arr() 函数实现此功能,与 append_arr() 函数相似,需要在数组已满时”自动扩容“;
void insert_arr(Arr * pArr, int pos, int val);
int main(void)
{
Arr arr;
int length = 2;
init_arr(&arr, length);
append_arr(&arr, 2);
append_arr(&arr, 7);
insert_arr(&arr, 2, 3);
show_arr(&arr);
return 0;
}
void insert_arr(Arr * pArr, int pos, int val)
{
if ( is_full(pArr) )
{
int * p;
int length = pArr->len + unit;
p = (int *)malloc(sizeof(int) * length);
if ( p == NULL )
{
printf("动态内存分配失败!!!\n");
exit(-1);
}
else
{
for ( int i=0;i<pos-1;i++ )
{
p[i] = pArr->pBase[i];
}
p[pos-1] = val;
pArr->cnt++;
for ( int i=pos;i<pArr->cnt;i++ )
{
p[i] = pArr->pBase[i-1];
}
free(pArr->pBase);
pArr->pBase = p;
pArr->len = length;
}
}
else
{
for ( int i=pArr->cnt;i>pos-1;i-- )
{
pArr->pBase[i] = pArr->pBase[i-1];
}
pArr->pBase[pos-1] = val;
pArr->cnt++;
}
return;
}
- 此时数组已经可以随意添加任意的元素了,这就足够了吗?当然不够,光增不减可不行,还需要一个删除元素的功能,delete_arr() 可以删除指定位置的元素,并告诉用户删除掉的值是多少以及数组剩下多少个有效值;
void delete_arr(Arr * pArr, int pos);
int main(void)
{
Arr arr;
int length = 2;
init_arr(&arr, length);
append_arr(&arr, 2);
append_arr(&arr, 7);
insert_arr(&arr, 2, 3);
show_arr(&arr);
delete_arr(&arr, 3);
show_arr(&arr);
return 0;
}
void delete_arr(Arr * pArr, int pos)
{
int val;
if ( is_empty(pArr) )
{
printf("数组为空!!!删除失败\n");
}
else
{
if ( pos <= pArr->cnt)
{
val = pArr->pBase[pos-1];
for ( int i=pos-1;i<pArr->cnt-1;i++ )
{
pArr->pBase[i] = pArr->pBase[i+1];
}
pArr->cnt--;
printf("删除掉的数字是 %d, 数组还剩下 %d 个有效值\n",val,pArr->cnt);
}
else
{
printf("删除位置大于当前数组有效长度!!!删除失败\n");
}
}
return;
}
- 有了基本的增减元素功能之后,还需要一些进阶功能才能使得数组变得易用,先来个倒置的功能吧,inverse_arr() 函数可以将当前数组的有效值顺序取反,由”正序变为反序“;
void inverse_arr(Arr * pArr);
int main(void)
{
Arr arr;
int length = 2;
init_arr(&arr, length);
append_arr(&arr, 2);
append_arr(&arr, 7);
insert_arr(&arr, 2, 3);
show_arr(&arr);
delete_arr(&arr, 3);
inverse_arr(&arr);
show_arr(&arr);
return 0;
}
void inverse_arr(Arr * pArr)
{
int t;
int i = 0;
int j = (pArr->cnt)-1;
while ( i < j )
{
t = pArr->pBase[i];
pArr->pBase[i] = pArr->pBase[j];
pArr->pBase[j] = t;
i++;
j--;
}
return;
}
- 有了各种花里胡哨的功能之后,数组成功的被用户给弄乱了,这时候又需要整理整个数组,sort_arr() 函数可以将数组按照从小到大的顺序排列,排序算法后面学了再来补充
void sort_arr(Arr * pArr);
int main(void)
{
Arr arr;
int length = 2;
init_arr(&arr, length);
append_arr(&arr, 2);
append_arr(&arr, 7);
insert_arr(&arr, 2, 3);
show_arr(&arr);
delete_arr(&arr, 3);
inverse_arr(&arr);
show_arr(&arr);
sort_arr(&arr);
return 0;
}
void sort_arr(Arr * pArr)
{
int t;
for ( int i=0;i<pArr->cnt;i++ )
{
for ( int j=i+1;j<pArr->cnt;j++ )
{
if ( pArr->pBase[j] < pArr->pBase[i] )
{
t = pArr->pBase[i];
pArr->pBase[i] = pArr->pBase[j];
pArr->pBase[j] = t;
}
}
}
}
- 到这里我自己 diy 的一个简单的数组就完成了,期待链表的学习,因为这个可变数组的缺陷很明显,如果数组太大的话,申请一块内存再重写的方式很鸡肋,甚至在释放掉原数组之前还可能造成内存占用过大!