C语言数据结构笔记:动态数组
前言本文面向编程零基础小白用生活化案例通俗讲解C语言中动态数组核心概念、组成要素与完整实操流程手把手演示动态数组的完整可运行代码示例。一、核心概念指针变量指的是可以储存某个内存地址的变量。一般来说指针变量并不储存某个有效数据而是指向某个存了有效数据的变量所在的地址可以指向一个变量的内存地址也可以指向一个结构体的内存地址甚至可以存另一个指针的的内存地址。但一般不会用它来储存有效数据。结构体结构体可以把一些变量“打包”成一个整体为的是在传递参数时可以通过直接传递结构体的指针从而将结构体内所有的变量一次性全部传入以方便使用、避免混淆。C语言的结构体在一定程度上有些类似 Java 的类class但是 Java 的类中可以有变量与函数方法C语言的结构体中只能有变量是不允许有函数的。内存分配在C语言中可以通过头文件 stdlib.h 中的 malloc 函数来申请分配一些内存然后将这些内存的地址赋值给一个指针。格式就像是//分配一块大小是一个 int 的内存//并将这块内存的地址赋值给指针 pint*p(int*)malloc(sizeof(int));//但 malloc 只负责给内存不负责放数据。内存里是垃圾值必须自己赋值。//通过指针为它指向的内存赋值*p10;//用完后记得用 free 函数释放这些内存free(p);//让 p 指向 null防止野指针pNULL;函数本质上是一段能重复调用的代码。C语言中的函数由 返回类型、函数名、参数列表、函数体、返回值 这五个部分组成。intplus(inta,intb){intcab;returnc;}在这个函数中int 是返回类型表示这个函数最终会返回一个整数。plus 是函数名也就是这个函数的名字。名字叫什么都可以除了关键字或用数字开头。函数名后面紧跟的括号以及括号中的内容就是这个函数需要的参数调用时必须传入与定义函数时相同类型、相同数量的参数。在大括号内的 “int c a b;” 就是这个函数的函数体了在调用这个函数时就会执行函数体。最后的 return c; 就是将 c 的值返回。定义一个函数并实际调用运行#includestdio.h//定义一个函数它需要两个整数作为参数intplus(inta,intb){//函数中为变量 c 进行了赋值intcab;//最后返回了变量 c 的值returnc;}//主函数程序执行入口intmain(){//调用 plus 函数为变量 i 赋值intiplus(15,20);//输出一下看看printf(%d,i);return0;}输出的结果就是35最后主函数是特殊的。在C语言中主函数的返回类型必须是 int函数名必须是 main参数列表必须是空的或者写(int argc, char *argv[])返回值一般写0。按理说可以写任何数字不过一般习惯返回0表示程序正常结束动态数组在C语言中数组的长度一旦确定便无法更改没有所谓的“加长”或“缩短”的操作。有些情况下如果数组拥有“扩容”的功能的话会方便很多比如我需要写一个哈希表自然会希望有一个可以自动扩容的数组。遗憾的是与 Java 不同C语言中并没有内置的官方动态数组给你用。但我们可以自己写。所谓的扩容功能我们可以通过新建一个比原来大的数组再把原来的数据全部拷贝过去最后让原来的指针指向新数组。不管过程怎么样我们的确实现了扩容的目的。之后还需要写添加、删减、获取数据等功能函数让使用者通过函数操作数组而不是直接操作内部数据安全、便捷地使用动态数组。二、指针的组成要素C语言的指针十分复杂。指针是什么指针是一种变量它一般被用来储存一块内存的地址。指针的类型指针是有类型区分的比如 int*整型指针或 char*字符指针甚至 void*无明确类型指针。指针的类型决定了它将以什么样的形式对指向内存的数据解引用、前后移操作、加减时走多远。比如对 int* 指针进行加减1时会往后走4个字节-1时会往前走4个字节。对 char* 指针1时会往后走1个字节-1时会往前走1个字节。而 void* 指针是例外因为并没有明确指向内存的数据是什么类型所以也不能进行增减操作、不能解引用。指针能做什么修改外部值调用函数时将外部的指针通过传入参数的方式传入函数中就可以在调用方函数后直接修改对应的外部变量数据。因为这本质上是将外部变量的内存地址传入了函数内部函数内外的指针指向的都是同一块内存修改的也是同一份数据。如果只是传入变量而非指针则相当于只是将变量复制了一份副本进入函数中这与外部对应的变量指向的并非同一块内存自然也非同一份数据。指针运算有明确类型的指针可以通过直接用 或 - 符号运算。比如一个字符数组char h[] “hello”;我们可以定义一个指针char *p h;然后通过 p 访问到字符 ‘e’也就是数组的第二位字符。指针也可以用大于号 、小于号 进行比较。通过对指针的运算也可以实现遍历数组的操作charh[]hello;//定义数组//定义指针char*ph;//这个操作会让指针指向数组的第一个位置//循环遍历for(inti0;i5;i){printf(%c,*(pi));}//另一种更老式、更接近底层的方式char*endh5;//定义一个指针表示数组末尾//直接通过比较指针来循环while(pend){printf(%c,*p);p;}当做数组使用数组本质上就是一串连续的内存。通过 malloc 函数申请分配一段连续的内存再将这段连续的内存第一格的地址赋值给一个指针变量本质上与创建数组一样。需要对这些内存进行操作时比如为其中一格赋值只需要指向那串内存的头指针就可以通过对指针增减来操作对应的位置。本质上与数组没有区别。多级指针指针可以指向指针定义多级指针时要加上两个*星号。通过这个操作我实现了可以储存任何数据类型的动态数组。原理是动态数组中每个格子都存 void*每个 void* 指向一个基本类型的指针比如一个 int*。然后这个声明了类型的指针再指向最终数据。这与 Java 中的 Object 数组十分相似不同的是Java 在解引用时会自动分辨类型但C语言中的 void* 做不到。三、完整实操案例#includestdbool.h//提供 bool、true、false#includestdio.h#includestdlib.h//动态数组结构体//data 存指针数组size 是当前元素个数len 是当前容量typedefstructArrayList{void**data;intsize;intlen;}ArrayList;//初始化数组指定初始容量ArrayList*arrayListCreate(intlen){//给结构体本身分配内存ArrayList*list(ArrayList*)malloc(sizeof(ArrayList));//给指针数组分配内存每个格子存一个 void*list-data(void**)malloc(len*sizeof(void*));list-size0;list-lenlen;returnlist;}//默认长度为10ArrayList*arrayListCreateDefault(){returnarrayListCreate(10);}//销毁数组//freeFn是用来释放每个元素的函数传 NULL 表示不释放元素voidarrayListDestroy(ArrayList*list,void(*freeFn)(void*)){//如果传了释放函数就逐个释放元素if(freeFn!NULL){for(inti0;ilist-size;i){freeFn(list-data[i]);}}//先释放里面的指针数组再释放结构体本身free(list-data);free(list);}//末尾添加voidarrayListAdd(ArrayList*list,void*value){//满了就扩容if(list-sizelist-len){intnewlenlist-len*2;void**newdata(void**)malloc(newlen*sizeof(void*));//把旧数据复制到新数组for(inti0;ilist-size;i){newdata[i]list-data[i];}//释放旧的指向新的free(list-data);list-datanewdata;list-lennewlen;}//放到末尾size 加1list-data[list-size]value;list-size;}//插入添加在 index 位置插入 valuevoidarrayListAddAt(ArrayList*list,intindex,void*value){//下标不合法就直接返回if(index0||indexlist-size){return;}//满了就扩容逻辑和 add 一样if(list-sizelist-len){intnewlenlist-len*2;void**newdata(void**)malloc(newlen*sizeof(void*));for(inti0;ilist-size;i){newdata[i]list-data[i];}free(list-data);list-datanewdata;list-lennewlen;}//从后往前挪给 index 腾出位置for(intilist-size-1;iindex;i--){list-data[i1]list-data[i];}//插入并 size 加1list-data[index]value;list-size;}//删除指定位置返回被删的指针void*arrayListRemoveAt(ArrayList*list,intindex){//下标不合法返回NULLif(index0||indexlist-size){returnNULL;}//先记住要删的void*removelist-data[index];//从前往后挪把后面的元素往前移一格for(intiindex1;ilist-size;i){list-data[i-1]list-data[i];}list-size--;returnremove;}//删除首个匹配的指针成功返回 true失败返回 falseboolarrayListRemove(ArrayList*list,void*value,void(*freeFn)(void*)){for(inti0;ilist-size;i){//泛型版只能比地址不能比内容if(list-data[i]value){//先释放元素再删除位置freeFn(list-data[i]);arrayListRemoveAt(list,i);returntrue;}}returnfalse;}//删除所有匹配的指针成功返回 true失败返回 falseboolarrayListRemoveAll(ArrayList*list,void*value,void(*freeFn)(void*)){bool removefalse;//从后往前删避免漏删for(intilist-size-1;i0;i--){if(list-data[i]value){freeFn(list-data[i]);arrayListRemoveAt(list,i);removetrue;}}returnremove;}//获取指定位置的指针void*arrayListGet(ArrayList*list,intindex){//下标不合法返回 NULLif(index0||indexlist-size){returnNULL;}returnlist-data[index];}//获取数组大小intarrayListSize(ArrayList*list){returnlist-size;}四、个人收获总结前段时间我在学习 Java 时也仿照 Java 标准库中的动态数组类写了一个动态数组用来学习所以这次用C语言练习时十分顺利。这次练习最难的一点是实现全类型通用。最开始我写的是 int 类型的动态数组先把功能都实现了再说。在 Java 中可以通过泛型或 Object 数组来实现全类型通用我用泛型实现了全类型通用的动态数组。但C语言中并没有泛型要实现全类型通用会比较麻烦但并非不可能。通过二级指针 void**实现了类似于 Java 中 Object 数组的效果数组中每一个位置都存二级指针 void**再通过二级指针指向我们需要的类型的指针最后再指向数据。不过需要注意的是在使用时需要先强制转型void* 并不能做到直接解引用。最后如果感兴趣可以看看 Java 版本的动态数组点击跳转Java数据结构基础笔记动态数组、链表

相关新闻

4核8G云服务器部署GitLab内存耗尽解决方案

4核8G云服务器部署GitLab内存耗尽解决方案

文章目录前言原因解决方案创建Swap分区修改配置文件前言 阿里云官方给出的方案是4核8G为最小部署Gitlab的配置,实际上配置完成后,云服务器的内存资源几乎被耗尽,本文将会给出对应的解决方案 原因 可以先通过 free -h 查看内存占用情况&…

2026/10/11 1:54:43 阅读更多 →
接口自动化 登录实现 pymysql

接口自动化 登录实现 pymysql

接口自动化:url:请求地址params:用于提交键值对数据,在请求行提交,适用于get和delete请求data:用于提交JSON数据,在请求提交,适用于POST和PUT请求json:用于提交JSON数据&…

2026/10/11 1:54:43 阅读更多 →
CIFAR10.zip 实战指南:从 pickle 解析到 CNN 基线训练与调参避坑

CIFAR10.zip 实战指南:从 pickle 解析到 CNN 基线训练与调参避坑

简介:这份资源面向正在入门神经网络与深度学习的学习者,围绕CIFAR10图像分类任务提供一套可直接运行的卷积神经网络实践方案,帮助解决从数据集理解到模型训练、验证的完整流程问题。压缩包共6个文件,约2.82MB,包含2个P…

2026/10/11 1:54:43 阅读更多 →

最新新闻

自建Docker镜像仓库完整指南:从选型到落地的踩坑总结

自建Docker镜像仓库完整指南:从选型到落地的踩坑总结

在容器化落地走到一定规模之后,几乎每个团队都会遇到一个绕不开的基础设施问题:镜像仓库。项目标题就四个字“docker镜像仓库”,但真正动手自建过的人都知道,这四个字背后藏着选型、存储、安全、性能、运维一长串的决策链。这篇就…

2026/10/11 2:44:13 阅读更多 →
RoboMaster机器人硬件设计从电源到CAN总线再到电机驱动的排查指南

RoboMaster机器人硬件设计从电源到CAN总线再到电机驱动的排查指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 2:44:13 阅读更多 →
错误面板可优化清单记录

错误面板可优化清单记录

代码阅读总结 这是中望CAD插件里质检结果展示的 UserControl(QCResultUserControl),基于WinForm,核心功能: 两个构造:无参构造用于插件启动预创建控件;带dwg路径构造直接加载图纸质检数据UI&…

2026/10/11 2:44:13 阅读更多 →
C语言算法分析

C语言算法分析

本文通过讲解洛谷中珠心算测验的解题思路带编程小白了解C语言算法&#xff0c;同时会介绍一些函数知识和字符的使用方法&#xff0c;希望大家能够通过这篇文章学到更多编程知识&#xff0c;从而可以更好地运行代码。 一、函数名称及作用 <string.h> 常用函数 函数 …

2026/10/11 2:44:13 阅读更多 →
华为鸿蒙免费戒烟工具—小羊戒烟

华为鸿蒙免费戒烟工具—小羊戒烟

午饭刚放下筷子&#xff0c;手又往烟盒那边伸——饭后一支烟像按了开关。有时硬生生忍住了&#xff0c;过一会儿却忘自己撑过几回&#xff1b;周末回想&#xff0c;只剩“好像少抽了”&#xff0c;本周到底比上周少几支、省了多少&#xff0c;说不清。我想把抽了几支、忍住几次…

2026/10/11 2:44:13 阅读更多 →
ArcGIS属性表字段添加与编辑实战:类型选择、计算器及维护指南

ArcGIS属性表字段添加与编辑实战:类型选择、计算器及维护指南

1. 字段类型没选对&#xff0c;后面全是坑&#xff1a;先把数据需求想明白前天帮同事处理一份小区地块数据入库&#xff0c;忙活半小时后发现面积字段精度对不上&#xff0c;明明算好是123.45平方米&#xff0c;属性表里却挂着123.450000001。我问他当时添加字段选了什么类型&a…

2026/10/11 2:43:12 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介&#xff1a;基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码&#xff0c;面向计算机相关专业课程设计与期末大作业学生&#xff0c;以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程&#xff0c;…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程&#xff1a;键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化&#xff0c;十个新手有八个栽在"往输入框里填东西"这件事上&#xff1a;要么填不进去&#xff0c;要么填了一半&#xff0c;要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程&#xff1a;阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀&#xff1a;什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面&#xff0c;跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介&#xff1a;基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码&#xff0c;面向计算机相关专业课程设计与期末大作业学生&#xff0c;以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程&#xff0c;…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程&#xff1a;键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化&#xff0c;十个新手有八个栽在"往输入框里填东西"这件事上&#xff1a;要么填不进去&#xff0c;要么填了一半&#xff0c;要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程&#xff1a;阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀&#xff1a;什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面&#xff0c;跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →