数组函数封装,自己记得一些笔记和个人理解,更加完善。
线性表:表中的数据元素关系是一对一的关系
顺序表:将表中的数据依次存放在计算机内存中
一组地址连续的存储单元中的一种数据结构
基本操作:增删改查,数组是顺序组的一种表现,物理位置连续,逻辑位置连续。
1.增:
腾出位置用来存放插入的数据,如果空间不够的情况下,要做插入,会导致后面的数据溢出。
2.删
在尾部删除
在中部或者头部删除:
改与查过于简易,这里不做赘述。
顺序表的实现:(可变化)
对于该顺序表的创建过程:结合了结构体,指针,函数等多方面的内容,需要仔细推敲。
意味着pArr->pData=NULL;
parr->length==NULL
销毁:
函数内部操作的是实参的副本,不是实参本身。如果想通过函数修改外部变量(包括指针变量)的本身值,就需要传递它的地址。
一、为什么创建 / 初始化函数(createArr、initArrData)不需要二级指针?
这两个函数的目标不是修改外部指针变量本身,而是操作「指针指向的结构体对象」,用一级指针就足够了:
-
初始化函数(initArrData):传递一级指针
Arr* pArr- 主函数中传递的是
myArr(一级指针,指向结构体对象),函数内部的pArr是myArr的副本,但这两个指针(myArr和pArr)指向同一块结构体内存。 - 我们在函数中操作的是
pArr->arr、pArr->length(结构体成员),本质是通过指针副本修改「共同指向的结构体对象的内容」,不是修改myArr这个指针变量本身的值。 - 简单说:我只是想给 “指针指向的房子装修”,不是想 “更换房子的门牌号(指针本身的值)”,一级指针足够。
- 主函数中传递的是
-
创建函数(createArr):直接返回
Arr*指针- 这个函数是新建一块结构体内存,把内存地址返回给主函数,主函数用
myArr(一级指针)接收这个地址即可,不需要通过函数修改myArr本身,自然不需要二级指针。
- 这个函数是新建一块结构体内存,把内存地址返回给主函数,主函数用
二、为什么销毁函数(destroyArr)必须用二级指针Arr** pArrPtr?
销毁函数的目标之一是修改外部指针变量本身(把主函数的myArr置为NULL),这时候必须传递外部指针的地址(即二级指针):
- 我们的需求:销毁后,主函数中的
myArr必须变成NULL(避免野指针),也就是要修改myArr这个指针变量本身的值(不是修改它指向的内容)。 - 如果用一级指针
Arr* pArr传递:- 函数内部的
pArr是myArr的副本,此时执行pArr = NULL,修改的只是副本的值,主函数中的myArr仍然指向原来被free的无效内存,还是野指针。 - 简单说:想修改 “门牌号(指针本身)”,但只拿到了 “门牌号的复印件(指针副本)”,修改复印件毫无意义。
- 函数内部的
- 用二级指针
Arr** pArrPtr传递:- 传递的是
&myArr(主函数中myArr指针变量的地址),函数内部的pArrPtr指向myArr。 - 执行
*pArrPtr = NULL,本质是通过 “指针地址” 直接修改主函数中myArr本身的值,让它变成NULL,达到避免野指针的目的。 - 简单说:拿到了 “门牌号的存放地址”,可以直接修改门牌号本身,让它变成无效(
NULL)。
- 传递的是
总结:一级指针 vs 二级指针的使用边界(针对结构体操作)
| 操作目标 | 所需指针类型 | 核心原因 |
|---|---|---|
修改结构体成员(arr、length) | 一级指针(Arr*) | 只需访问指针指向的结构体内存,无需修改指针本身 |
修改外部指针变量本身(置为NULL) | 二级指针(Arr**) | 需通过指针地址修改外部指针的值,克服值传递的副本限制 |
简单类比:
- 一级指针:你有一把家门钥匙(
myArr),复制了一把(pArr),用复制的钥匙也能打开家门、修改家里的东西(结构体成员)。 - 二级指针:你想把原来的家门钥匙作废(置为
NULL),必须找到存放原钥匙的盒子(&myArr),二级指针就是打开这个盒子的钥匙。

为什么必须单独释放(*pArr)->pData?
(*pArr)->pData是动态分配的内存(比如通过malloc申请的数组),它的内存和结构体*pArr本身的内存是分开的:
*pArr的内存:是结构体对象的内存(比如用malloc(sizeof(ArrStruct))申请的);(*pArr)->pData的内存:是结构体内部指针单独申请的内存(比如(*pArr)->pData = malloc(10 * sizeof(int)))。
如果只释放*pArr(结构体本身),pData指向的内存会变成无人管理的 “内存泄漏”—— 因为结构体释放后,再也找不到pData的地址,没法释放这部分内存了。
为什么length不用单独释放?
length是结构体的普通成员变量(不是指针),它的内存是包含在结构体*pArr的内存里的。当你执行free(*pArr)释放结构体本身时,length占用的内存会随着结构体一起被系统回收,不需要单独用free(free只能用来释放动态分配的内存,普通成员变量不需要)。
因为函数型参与实参的区别,用二级指针的话可以实现对于实参的真正销毁。
且两者都有释放的意义;

create函数的返回类型是Arr*,但你写了return -1;(-1是int类型),这是类型不匹配,编译会报错。应该改成return NULL;。
清除:与销毁不同,只是把原来赋予的值清除掉。

判断数据表是否为空:
添加数据:
1.尾部追加函数


你这个需求用realloc是更高效的(不用新申请 + 拷贝)
realloc更好!不会造成内存的浪费,使用realloc的一些注意事项:
- 必须用临时指针接收返回值:如果
realloc失败,会返回NULL,如果直接用p->arr = realloc(...),失败时会导致原p->arr的地址丢失(内存泄漏); - 原指针为
NULL的兼容:如果p->arr是NULL(比如首次添加元素),realloc会自动等价于malloc,不用单独写malloc的逻辑; - 内存扩充的逻辑顺序:先扩内存→检查是否成功→再更新指针 / 长度 / 添加元素,避免 “长度先增加但内存没扩成” 的逻辑错误。
2.插入位置函数


3.删除数据
A:尾部删除
B:头部/中部删除


查找

‘
补:笔记有误,在return 后自行添加-1;
改写:

以下是完整代码示例
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
_CRT_SECURE_NO_WARNINGS
typedef struct Myarr
{
int* arr;
int length;
}Arr;
//创建结构体
Arr* creat(void)
{
Arr* p = (Arr*)malloc(sizeof(Arr));
if (p == NULL)
{
printf("创建失败");
return NULL;
}
return p;
}
void creatarr(Arr* p, int length)
{
p->arr = (int*)malloc(sizeof(int) * length);
p->length = length;
}//子函数的赋值函数的封装;
//创建函数的封装,为其赋予内存和内容;d
void destroy(Arr** p)
{
if (NULL == (*p))
{
printf("本身不存在,无需销毁");
return;
}
if (NULL == (*p)->arr)
printf("无需销毁,数组不存在");
free(*p);
*p = NULL;
free((*p)->arr);
(*p)->arr = NULL;
}
//创建函数的销毁,从根本上清除,需要二级指针。
void clear(Arr* p)
{
if (NULL == p)
{
printf("本身不存在,无法清除");
return;
}
if (NULL == p->arr)
{
printf("数组为空,无需要清除");
return;
}
free(p->arr);
p->arr = NULL;
p->length = NULL;
}
//创建函数的清除
void iskong(Arr* p)
{
if (NULL == p)
{
printf("本身不存在");
}
if (NULL == p->arr)
{
printf("该数组为空");
}
printf("不为空\n");
}
//判断数组是否为空;
void putlast(Arr* p, int target)
{
if (NULL == p)
{
printf("本身不存在");
return;
}
int* newarr = realloc(p->arr, sizeof(int) * (p->length + 1));
if (newarr == NULL)
{
printf("扩充内存失败");
return;
}
p->arr = newarr;
p->arr[p->length] = target;
p->length++;
}//在数组的末尾添加函数;
void putmid(Arr* p, int ip, int target)
{
if (NULL == p)
{
printf("本身不存在");
return;
}
if (ip<0 || ip>p->length)
{
printf("ip不合法");
return;
}
int* newarr = (int*)realloc(p->arr, sizeof(int) * (p->length + 1));
if (newarr == NULL)
{
printf("扩充内存失败");
return;
}
p->arr = newarr;
for (int i = p->length; i >= ip;i--)
p->arr[i] = p->arr[i - 1];
p->arr[ip] = target;
p->length++;
}//在数组中间增删改,其实有这个就够了,包含在尾部更好操作。
int search(Arr* p, int target)
{
if (NULL == p)
{
printf("本身不存在");
return -1;
}
for (int i = 0;i < p->length;i++)
{
if (p->arr[i] == target)
return i;
}
}
//查找函数,并返回其ip下标;
void delete(Arr* p, int target)
{
if (NULL == p)
{
printf("本身不存在");
return;
}
int r = search(p, target);
if (r == -1)
{
printf("不存在该值,无法删除");
return;
}
for (int i = r;i < p->length - 1;i++)
p->arr[i] = p->arr[i + 1];
int* newarr = (int*)realloc(p->arr, sizeof(int) * (p->length - 1));
if (newarr == NULL)
{
printf("收缩内存失败");
return;
}
p->arr = newarr;
p->length--;
}//在中部删减数据,其实在末尾也适用,所以末尾的就先不写了,🤫;
void change(Arr* p, int from, int target)
{
if (NULL == p)
{
printf("本身不存在");
return;
}
int r = search(p, from);
if (r == -1)
{
printf("不存在该值,无法修改");
return;
}
p->arr[r] = target;
}//对数组中数值的改写;
void printd(Arr* p)
{
for (int i = 0;i < p->length;i++)
printf("%d", p->arr[i]);
printf("\n");
}
//接下来是应用时刻;
int main()
{
Arr *p = creat();
creatarr(p, 5);
for (int i = 0;i < p->length;i++)
p->arr[i] = i + 1;
/*destroy(p);*/
/*clear(p);*/
putlast(p, 6);
iskong(p);
putmid(p, 0, 0);
delete(p, 1);
printd(p);
int s = search(p, 0);
printf("%d", s);
return 0;
}
更多推荐

所有评论(0)