7.1 C/C++ 实现动态数组
动态数组相比于静态数组具有更大的灵活性,因为其大小可以在运行时根据程序的需要动态地进行分配和调整,而不需要在编译时就确定数组的大小。这使得动态数组非常适合于需要动态添加或删除元素的情况,因为它们可以在不浪费空间的情况下根据需要动态增加或减少存储空间。
动态数组的内存空间是从堆(heap)上分配的,动态数组需要程序员手动管理内存,因为它们的内存空间是在程序运行时动态分配的。程序员需要在使用完动态数组后手动释放其内存空间,否则可能会导致内存泄漏的问题,进而导致程序崩溃或者运行缓慢。因此,在使用动态数组时,程序员需要特别注意内存管理的问题。
读者需自行创建头文件dynamic.h并拷贝如下动态数组代码实现;
#include <stdlib.h>
#include <string.h>
struct DynamicArray
{
void **addr; // 存放元素或结构体的首地址
int curr_size; // 存放当前元素数量
int max_size; // 存放当前最大元素数
};
// 初始化动态数组,初始化后直接返回数组的首地址
struct DynamicArray *InitDynamicArray(int size)
{
// 如果小于0则说明没有元素,返回NULL
if (size <= 0)
{
return NULL;
}
// 分配结构指针,此处分配的是结构体指针,并没有分配空间
struct DynamicArray *ptr = malloc(sizeof(struct DynamicArray));
if (ptr != NULL)
{
// 将当前元素索引设置为0
ptr->curr_size = 0;
// 默认最大数组元素数为size
ptr->max_size = size;
// 实际分配存储空间大小是max_size最大元素
ptr->addr = malloc(sizeof(void *) * ptr->max_size);
return ptr;
}
return NULL;
}
// 将元素插入到指定位置
void InsertDynamicArray(struct DynamicArray *ptr, int index, void *data)
{
// 判断如果数组不为空,或者是data不为空,则继续执行
if (ptr != NULL || data != NULL)
{
// 如果插入位置小于当前0,或者大于当前元素总个数
if (index < 0 || index > ptr->curr_size)
{
// 就自动把它插入到元素的末尾位置
index = ptr->curr_size;
}
// 紧接着判断当前元素数是否大于最大值,大于则分配空间
if (ptr->curr_size >= ptr->max_size)
{
// 分配一块更大的空间,这里分配原始空间的2倍
int new_max_size = ptr->max_size * 2;
void **new_space = malloc(sizeof(void *) * new_max_size);
// 接着将原来空间中的数据拷贝到新分配的空间
memcpy(new_space, ptr->addr, sizeof(void *) * ptr->max_size);
// 释放原来的内存空间,并更新指针的指向为新空间的地址
free(ptr->addr);
ptr->addr = new_space;
ptr->max_size = new_max_size;
}
// 开始移动元素,给ins元素腾出空来
for (int x = ptr->curr_size - 1; x >= index; --x)
{
// 从后向前,将前一个元素移动到后一个元素上
ptr->addr[x + 1] = ptr->addr[x];
}
// 设置好指针以后,开始赋值
ptr->addr[index] = data;
ptr->curr_size++;
return 1;
}
return 0;
}
// 遍历数组中的元素,这里的回调函数是用于强制类型转换,自定义输出时使用
void ForeachDynamicArray(struct DynamicArray *ptr, void(*_callback)(void *))
{
if (ptr != NULL || _callback != NULL)
{
for (int x = 0; x < ptr->curr_size; x++)
{
// 调用回调函数并将数组指针传递过去
_callback(ptr->addr[x]);
}
}
}
// 根据位置删除指定元素,index = 元素的下标位置
void RemoveByPosDynamicArray(struct DynamicArray *ptr, int index)
{
if (ptr == 0)
return 0;
// 判断当前插入位置index必须大于0且小于curr_size
if (index > 0 || index < ptr->curr_size - 1)
{
for (int i = index; i < ptr->curr_size - 1; ++i)
{
// 每次循环都将后一个元素覆盖到前一个元素上
ptr->addr[i] = ptr->addr[i + 1];
}
// 最后当前元素数量应该减去1
ptr->curr_size--;
}
}
// 按照元素的指定值进行元素删除,这里需要回调函数指定要删除元素的值是多少
void RemoveByValueDynamicArray(struct DynamicArray *ptr, void *data, int(*compare)(void*, void *))
{
if (ptr != NULL && data != NULL && compare != NULL)
{
for (int i = 0; i < ptr->curr_size; ++i)
{
if (compare(ptr->addr[i], data))
{
RemoveByPos_DynamicArray(ptr, i);
break;
}
}
}
}
// 销毁数组
void DestroyDynamicArray(struct DynamicArray *ptr)
{
if (ptr != NULL)
{
if (ptr->addr != NULL)
{
free(ptr->addr);
ptr->addr = NULL;
}
free(ptr);
ptr = NULL;
}
}
上述代码的使用很容易,如下代码实现了动态数组的基本操作,包括创建动态数组、插入元素、删除元素、遍历元素和销毁动态数组。其中定义了一个自定义结构体Student,用于作为动态数组的元素。在使用InitDynamicArray函数创建动态数组之后,使用InsertDynamicArray函数将四个元素插入到动态数组中,其中第三个元素插入的位置为3。然后使用RemoveByPosDynamicArray函数根据下标移除第一个元素,使用RemoveByValueDynamicArray函数根据元素的值移除第二个元素,其中使用myCompare回调函数对比元素。最后使用ForeachDynamicArray函数遍历所有元素,并使用MyPrint回调函数输出元素的值。最终销毁动态数组,释放内存。
#include "dynamic.h"
// 自定义结构体
struct Student
{
int uid;
char name[64];
int age;
};
// 回调函数用于输出元素
void MyPrint(void *data)
{
// 强制类型转换,转成我们想要的类型
struct Student *ptr = (struct Student *)data;
printf("Uid: %d --> Name: %s \n", ptr->uid, ptr->name);
}
// 回调函数用于对比元素
int myCompare(void *x, void *y)
{
struct Student *p1 = (struct Student *)x;
struct Student *p2 = (struct Student *)y;
if (strcmp(p1->name, p2->name) == 0)
{
return 1;
}
return 0;
}
int main(int argc, char *argv[])
{
//创建动态数组
struct DynamicArray *ptr = InitDynamicArray(5);
// 创建元素
struct Student stu1 = { 1001, "admin1", 22 };
struct Student stu2 = { 1002, "admin2", 33 };
struct Student stu3 = { 1003, "admin3", 44 };
struct Student stu4 = { 1004, "admin4", 55 };
// 将元素插入到数组
InsertDynamicArray(ptr, 0, &stu1);
InsertDynamicArray(ptr, 1, &stu2);
InsertDynamicArray(ptr, 3, &stu3);
InsertDynamicArray(ptr, 4, &stu4);
// 根据下标移除元素
RemoveByPosDynamicArray(ptr, 0);
// 删除元素是p_delete的数据
struct Student p_delete = { 1002, "admin2", 33 };
RemoveByValueDynamicArray(ptr, &p_delete, myCompare);
// 遍历元素
ForeachDynamicArray(ptr, MyPrint);
// 销毁顺序表
DestroyDynamicArray(ptr);
system("pause");
return 0;
}
7.1 C/C++ 实现动态数组的更多相关文章
- 常用数据结构-线性表及Java 动态数组 深究
[Java心得总结六]Java容器中——Collection在前面自己总结的一篇博文中对Collection的框架结构做了整理,这里深究一下Java中list的实现方式 1.动态数组 In compu ...
- C语言 · 动态数组的使用
从键盘读入n个整数,使用动态数组存储所读入的整数,并计算它们的和与平均值分别输出.要求尽可能使用函数实现程序代码.平均值为小数的只保留其整数部分. 样例输入: 5 3 4 0 0 2样例输出:9 1样 ...
- C++中关于[]静态数组和new分配的动态数组的区别分析
这篇文章主要介绍了C++中关于[]静态数组和new分配的动态数组的区别分析,很重要的概念,需要的朋友可以参考下 本文以实例分析了C++语言中关于[]静态数组和new分配的动态数组的区别,可以帮助大家加 ...
- C++之动态数组
C99支持一种名为变长数组的结构来方便程序员.C++也提供了一种长度可在程序运行时确定的数组类型:动态数组.声明格式为:(声明 int 类型的数组) ; //此处可修改 ArraySize 的值 in ...
- VB默认属性、动态数组、Range对象的默认属性的一点不成熟的想法
1.默认属性 VB6.0有默认属性的特性.当没有给对象指定具体的属性时,"默认属性"是VB6.0将使用的属性.在某些情形下,省略常用属性名,使代码更为精简. 因为CommandBu ...
- C#有关数组内存的释放及动态数组问题
一.数组内存释放问题 数组内存的释放可以按照如下语句实现: string [] aa=new string[2]; aa[0]="A"; aa[1]="B"; ...
- (待续)C#语言中的动态数组(ArrayList)模拟常用页面置换算法(FIFO、LRU、Optimal)
目录 00 简介 01 算法概述 02 公用方法与变量解释 03 先进先出置换算法(FIFO) 04 最近最久未使用(LRU)算法 05 最佳置换算法(OPT) 00 简介 页面置换算法主要是记录内存 ...
- C++ 动态数组实例
一维动态数组的实例: #include <iostream> using namespace std; int main() { int *arr; int n; cout<< ...
- C++动态数组
一: 一维数组初始化 标准方式1:int value[100]; //value[i]的值不定,因为没有初始化:标准方式2:int value[100] = {1,2,3}; //value[0],v ...
- ALLOCATE语句分配FORTRAN动态数组方法(转自http://blog.csdn.net/zhuxianjianqi/article/details/8067174)
数组的动态分配 a) 可分配数组 数组可以是静态的也可以是动态的.如果数组是静态的,则在编译时就被分配了固定的储存空间,并且直到程序退出时才被释放.程序运行时静态数组的大小不能改变.静态数组的缺 ...
随机推荐
- 同步时间,为什么我选 Chrony 而不是 NTP ?
初识 chrony chrony 是网络时间协议(Network Time Protocol )的通用实现 它不但可以提供保持系统时间与 NTP 时钟服务器同步的服务,还能作为 NTP 服务器对其他服 ...
- IDEA整合SSM框架:简易图书操作模块
这个SSM整合文章,以图书的增删改查为例 本篇文章源码已上传: Github:https://github.com/RivTian/University-coursework/tree/master/ ...
- 【每日一题】14.Accumulation Degree(树形DP + 二次扫描)
补题链接:Here 一个树形水系,有 \(n\) 个结点,根结点称为源点,叶子结点称为汇点,每条边都有水量限制$C(x,y) \((\)x,y$ 为这条边的两个端点),源点单位时间流出的水量称为整个水 ...
- 14、SpringBoot-easyexcel导出excle
系列导航 springBoot项目打jar包 1.springboot工程新建(单模块) 2.springboot创建多模块工程 3.springboot连接数据库 4.SpringBoot连接数据库 ...
- 二、mysql安装(主从)
上一篇: 一.mysql5.7 rpm 安装(单机) https://www.cnblogs.com/yclh/p/14951314.html 环境 192.168.0.1 主节点 ...
- 2023年AI领域行业洞察,看这30个统计数据就够了!
PrimiHub一款由密码学专家团队打造的开源隐私计算平台,专注于分享数据安全.密码学.联邦学习.同态加密等隐私计算领域的技术和内容. 随着AIGC的爆火,企业越来越多地开始采用生成式人工智能.自然语 ...
- SQLServer数据库优化学习-总结
SQLServer数据库优化学习-总结 背景 各种能力都需要提升. 最近总是遇到SQLServer的问题 趁着周末进行一下学习与提高. 安装与优化 1. 数据库必须安装 64位, 不要安装成32位的版 ...
- [转帖]数据可视化之redash(支持43种数据源) (转自https://anjia0532.github.io/2019/07/08/redash/)
https://www.cnblogs.com/a00ium/p/13177272.html 人类都是视觉动物,讲究一图胜千言.如果没了可视化,那么你在跟领导汇报工作时,很大程度会鸡同鸭讲.其实 ex ...
- [转帖]Postmark - 存储性能测试工具
1. 引言 Postmark是由著名的NAS提供商NetApp开发,用来测试其产品的后端存储性能. Postmark主要用于测试文件系统在邮件系统或电子商务系统中性能,这类应用的特点是:需要频繁.大量 ...
- [转帖]Linux系统:page fault
Linux进程如何访问内存 Linux下,进程并不是直接访问物理内存,而是通过内存管理单元(MMU)来访问内存资源,原因后面会讲到. 为什么需要虚拟内存地址空间 假设某个进程需要4MB的空间,内存假设 ...