自制简易可变数组

自制可变数组

具有的功能:

  1. 初始化
  2. 判断是否为空
  3. 判断是否满
  4. 展示数组
  5. 在末尾追加值(若数组已满可自动增长)
  6. 在中间插入值
  7. 删除指定位置的值
  8. 倒置数组
  9. 整理数组

搭建(diy)过程

  1. 自定义一个结构类型 Arr,包含一个指针 pBase、数组总长度 len、数组有效长度 cnt;
  ~~~ C
typedef struct {
	int *pBase;
	int len;
	int cnt;
} Arr;

int main(void)
{
	Arr arr;
    return 0;
}
  • 指针 pBase 就类似普通数组中数组变量名的作用,对其进行操作就可以获得数组中的任一元素
  1. 有了结构类型 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); 
//使 pBase 指向一片长度为 length * sizeof(int)的空间,即此时 arr 中可以装 length 个 int                                  
	if ( pArr->pBase == NULL ) // 若 malloc 函数返回 NULL 值,说明内存不足,此时终止程序 
	{
		printf("动态内存分配失败!!!\n");
		exit(-1); //终止整个程序 
	}
	else
	{
		pArr->len = length; //将数组总长度保存在 Arr 类型的 len 中
		pArr->cnt = 0;      //将数组有效长度保存在 Arr 类型的 cnt 中,初始为 0
	}
	return;
}
  1. 有了初始化的功能后,如果要查看该数组,采用循环的方式就很不方便了,此时可以定义一个 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 ) // 对数组当前的有效数字进行判断,若为 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]); 
            // pArr->pBase[i] <<==>> *pArr->pBase
		}
		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;
}
  1. 有了在末尾添加元素的功能后,感觉还需要一个在数组中间插入元素的功能,不然感觉缺了一块,于是来定义一个 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)
{
    // pos 代表插入元素的位置,从 1 开始
    // val 代表插入元素的值
	if ( is_full(pArr) )
    // 如果数组已满,需要申请另一片大空间,对数组”扩容“,并分两次(插入位置前后)将原数组元素     // 写入新的数组空间
	{
		int * p;
		int length = pArr->len + unit;
		p = (int *)malloc(sizeof(int) * length);
		if ( p == NULL ) // 若 malloc 函数返回 NULL 值,
		                 // 说明内存不足,此时终止程序 
		{
			printf("动态内存分配失败!!!\n");
			exit(-1); //终止整个程序 
		}
		else
		{
			for ( int i=0;i<pos-1;i++ )
			{
				p[i] = pArr->pBase[i];
                // 将原数组元素位于插入位置之前的元素赋值到新的数组内存中
			}
			p[pos-1] = val;
            // 将 val 的值赋给对应位置
			pArr->cnt++;
            // 原数组 cnt 的值加 1,即有效数字加一
			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;
        // 将 val 的值赋给对应位置
		pArr->cnt++;
        // 原数组 cnt 的值加 1,即有效数字加一
	}
	return;
}
  1. 此时数组已经可以随意添加任意的元素了,这就足够了吗?当然不够,光增不减可不行,还需要一个删除元素的功能,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]; 
            // 先将这个值保存到变量 val 中,之后要告诉用户
			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;
}
  1. 有了基本的增减元素功能之后,还需要一些进阶功能才能使得数组变得易用,先来个倒置的功能吧,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)
{
    // 倒置函数将由一个倒置算法实现:
    // 其中 i 为数组初始序列,即为 0, j 为数组最后序列,为 cnt-1
    // t 用来保存值,理解成 temp 
	int t;
	int i = 0;
	int j = (pArr->cnt)-1;
	while ( i < j )
    // 用 while 循环来进行这个过程,整个过程如下:
    // 将数组头部的值赋给 t ,将数组尾部的值赋给数组头部,将 t 的值赋给数组尾部
    // 实现一次交换,随后将头部指针向后移一位,尾部指针向前移一位,重复上述过程,直到前面的指针大于后面的指针就停止整个过程
	{
		t = pArr->pBase[i];
		pArr->pBase[i] = pArr->pBase[j];
		pArr->pBase[j] = t;
		i++;
		j--;
 	}
 	return;
}
  1. 有了各种花里胡哨的功能之后,数组成功的被用户给弄乱了,这时候又需要整理整个数组,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;
			}
		}
	}
}
  1. 到这里我自己 diy 的一个简单的数组就完成了,期待链表的学习,因为这个可变数组的缺陷很明显,如果数组太大的话,申请一块内存再重写的方式很鸡肋,甚至在释放掉原数组之前还可能造成内存占用过大!
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值