🎈个人主页:豌豆射手^
🎉欢迎 👍点赞✍评论⭐收藏
🤗收录专栏:数据结构
🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步!
【王道数据结构笔记】顺序表的动态分配代码分析
- 引言
- 一 代码
- 二代码分析
- 步骤1:
- 步骤2:
- 步骤3:
- 步骤4:
- 步骤5:
- 步骤6:
- 步骤7:
- 总结
引言
一 代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
#define InitSize 10
typedef struct {
int *data;
int MaxSize;
int length;
}SeqList;
void InitList(SeqList& L)
{
L.data = (int*)malloc( InitSize * sizeof(int));
L.length = 0;
L.MaxSize = InitSize;
}
void IncreaseSize(SeqList& L, int len)
{
int* p = L.data;
L.data = (int*)malloc((L.MaxSize + len) * sizeof(int));
for (int i = 0; i < L.length; i++)
{
L.data[i] = p[i];
}
L.MaxSize = L.MaxSize + len;
free(p);
}
int main()
{
SeqList L;
InitList(L);
IncreaseSize(L, 5);
}
二代码分析
下面是拆分后的代码,以及对每一步骤的分析:
步骤1:
#define _CRT_SECURE_NO_WARNINGS
分析:
定义了一个宏_CRT_SECURE_NO_WARNINGS
,用于消除某些编译器关于安全性的警告,例如在使用malloc
等函数时可能出现的警告。
步骤2:
#include <stdio.h>
#include <stdlib.h>
分析:
包含了标准输入输出库stdio.h
和标准库stdlib.h
,前者用于输入输出操作,后者提供了内存分配函数如malloc
和free
。
步骤3:
#define InitSize 10
分析:
定义了一个宏InitSize
,值为10,用于初始化顺序表(SeqList)的初始大小。
步骤4:
typedef struct {
int *data;
int MaxSize;
int length;
} SeqList;
分析:
定义了一个结构体类型SeqList
,包含一个整型指针data
用于存储数据,整型MaxSize
用于存储顺序表的最大容量,整型length
用于存储顺序表当前的长度。
指针data
在结构体类型
SeqList
中,整型指针data
用于存储顺序表中实际数据的内存地址。具体来说,data
是一个指向整型数据的指针,它指向一个动态分配的内存区域,该区域用于存储顺序表中的元素。
在顺序表中,我们通常不知道事先需要存储多少个元素,或者元素的数量可能会在运行过程中变化。因此,使用动态内存分配(如
malloc
)来分配存储空间是一个灵活且有效的方法。data
指针就是用来管理这块动态分配的内存的。
当创建或初始化一个
SeqList
对象时,我们通常会为data
指针分配一块足够大的内存空间来存储初始的元素。随着顺序表中元素的增加或减少,我们可能需要重新分配data
指针所指向的内存空间,以适应新的存储需求。这时,我们会使用realloc
或再次调用malloc
来重新分配内存,并更新data
指针的指向。
通过
data
指针,我们可以方便地访问和修改顺序表中的元素。例如,通过计算元素的索引和data
指针的偏移量,我们可以直接定位到某个元素的内存位置,并进行读取或写入操作。
总之,
data
指针在SeqList
结构体中起到了桥梁的作用,它连接了顺序表的逻辑结构和实际存储数据的内存空间。
整型Maxsize:
在SeqList
结构体中,整型MaxSize
用于表示顺序表当前分配的最大容量。这个值通常用于在动态内存管理中跟踪已经为顺序表分配了多少内存空间。
具体来说,MaxSize
的作用体现在以下几个方面:
-
内存分配参考:当需要增加顺序表的容量时(例如,通过调用
IncreaseSize
函数),MaxSize
会被用来计算需要分配多少额外的内存空间。这样,程序可以确保在扩展顺序表时分配足够的内存。 -
防止越界访问:在添加新元素到顺序表之前,通常会检查顺序表的当前长度是否已经达到
MaxSize
。如果达到或超过MaxSize
,那么程序会知道需要增加顺序表的容量,以避免越界访问内存,这是一种常见的内存安全错误。 -
优化内存使用:通过跟踪
MaxSize
,程序可以更有效地管理内存。例如,如果顺序表的大小经常变化,但变化不大,那么可以通过适当地调整MaxSize
来减少频繁的内存分配和释放操作,从而提高性能。 -
容量监控:
MaxSize
也可以用于监控顺序表的内存使用情况。通过比较MaxSize
和顺序表的当前长度,程序可以了解顺序表是否接近满载,从而决定是否需要进行容量调整。
优化内存具体来说是在实际应用中,如果顺序表(例如一个动态数组)的大小经常会在一个较小的范围内变动,那么频繁地进行内存分配和释放操作可能会成为性能瓶颈。
为了优化性能,我们可以采取一种策略,即不是每次顺序表大小稍有变化就立即重新分配内存,而是预留一些额外的空间,即适当增加MaxSize的值。
具体来说,当顺序表需要增加元素时,我们并不立即按照新的大小重新分配内存,而是检查当前预留的空间是否足够。如果足够,我们就不会进行内存分配操作,而是直接在预留的空间中添加新元素。
只有当预留的空间不足时,我们才会进行一次较大的内存分配操作,以满足未来一段时间内可能的增长需求。
这种策略可以减少内存分配和释放的次数,因为内存分配和释放通常涉及系统调用和可能的内存碎片问题,这些操作相对耗时。
通过适当调整MaxSize的值,我们可以在一定程度上平滑这些操作对性能的影响,从而提高程序的执行效率。
需要注意的是,预留过多的空间也会浪费内存资源,因此需要在性能和内存使用之间找到一个平衡。这通常需要根据具体的应用场景和需求来决定。
总的来说,MaxSize
在SeqList
结构体中是一个重要的元数据成员,它帮助程序更好地管理顺序表的内存使用,确保内存安全,并优化性能。
整型length:
在顺序表(如SeqList
)的数据结构中,整型length
通常用于表示顺序表当前实际存储的元素个数,也就是顺序表的当前长度。这个值对于顺序表的操作和管理至关重要,具体体现在以下几个方面:
-
元素访问:通过
length
,我们可以快速知道顺序表中当前有多少个元素,从而安全地访问这些元素。例如,当我们要读取或修改顺序表中的某个元素时,需要确保该元素的索引在有效范围内(即小于length
)。 -
添加和删除元素:当向顺序表中添加元素时,我们需要更新
length
以反映新添加的元素。同样地,当从顺序表中删除元素时,也需要减少length
的值。通过length
,我们可以方便地追踪顺序表的当前状态。 -
容量与长度比较:
length
和MaxSize
的比较是顺序表管理中常见的操作。通过比较这两个值,我们可以判断顺序表是否已满(即length
是否等于MaxSize
),从而决定是否需要进行扩容操作。 -
空间效率监控:
length
也用于监控顺序表的空间使用情况。例如,如果length
远小于MaxSize
,这可能意味着顺序表分配了过多的内存,存在空间浪费的情况。根据应用的需求,可以考虑是否需要进行内存收缩操作。
总之,整型length
在顺序表的数据结构中扮演了记录当前元素数量、辅助元素访问和修改、管理内存空间等重要角色。它是顺序表实现中不可或缺的一部分。
步骤5:
void InitList(SeqList& L) {
L.data = (int*)malloc(InitSize * sizeof(int));
L.length = 0;
L.MaxSize = InitSize;
}
分析:
定义了初始化顺序表的函数InitList
,它接受一个SeqList
类型的引用L
作为参数。函数内部使用malloc
为L.data
分配了InitSize
个整数的空间,最后将L.length
和L.MaxSize
分别初始化为0和InitSize
。
L.data = (int*)malloc(InitSize * sizeof(int));
这行代码是C语言中用于动态内存分配的一行关键代码,它用于为一个顺序表(在这里是SeqList
类型的对象L
)分配初始的内存空间。具体地,这一行代码做了以下几件事:
-
malloc(InitSize * sizeof(int))
:malloc
是一个标准库函数,用于在堆上动态分配指定字节大小的内存。InitSize
是一个变量(或常量),表示要分配的整型元素(int
)的数量。sizeof(int)
是一个运算符,返回int
类型在特定系统或编译器上所占用的字节数。InitSize * sizeof(int)
计算了总共需要分配的字节数。
-
(int*)
:- 这是一个类型转换(或称为强制类型转换)。
malloc
函数返回的是一个指向void
的指针(void*
),因为malloc
可以分配任何类型的内存。但是,在C语言中,我们不能直接将void*
类型的指针赋给其他类型的指针。因此,这里使用(int*)
将void*
类型的指针转换为int*
类型的指针。这样,我们就可以将这个指针赋值给指向整型的指针变量。
- 这是一个类型转换(或称为强制类型转换)。
-
L.data = ...
:L
是一个SeqList
类型的对象。L.data
是SeqList
结构体中的一个成员,它是一个指向整型的指针,用于存储顺序表数据的实际内存地址。- 这一行代码将
malloc
返回的指针(即新分配的内存地址)赋值给L.data
,这样L.data
就指向了新分配的内存区域。
综上所述,这一行代码的目的是为顺序表L
分配一个可以存储InitSize
个整数的内存区域,并将这块内存的地址赋值给L.data
。此后,顺序表L
就可以通过L.data
指针来访问和操作这些内存中的元素了。
需要注意的是,在使用malloc
分配内存后,应始终检查返回值是否为NULL
,以确保内存分配成功。此外,当不再需要这块内存时,应使用free
函数来释放它,以防止内存泄漏。
步骤6:
void IncreaseSize(SeqList& L, int len) {
int* p = L.data;
L.data = (int*)malloc((L.MaxSize + len) * sizeof(int));
分析:
定义了增加顺序表大小的函数IncreaseSize
,它接受一个SeqList
类型的引用L
和一个整数len
作为参数。首先,它保存了L.data
的当前地址到指针p
中,以便稍后释放这块内存。然后,它使用malloc
为L.data
重新分配了一块更大的内存空间,新的大小是原来的L.MaxSize
加上增加的len
个整数的大小。
步骤7:
for (int i = 0; i < L.length; i++) {
L.data[i] = p[i];
}
分析:
使用循环遍历原顺序表L
的每一个元素,并将这些元素复制到新分配的内存空间中。
步骤8:
L.MaxSize = L.MaxSize + len;
free(p);
}
分析:
更新顺序表L
的最大容量L.MaxSize
,然后释放原来L.data
指向的内存空间。
步骤9:
int main() {
SeqList L;
InitList(L);
IncreaseSize(L, 5);
}
分析:
定义了程序的主函数main
。首先,创建了一个SeqList
类型的变量L
,然后调用InitList
函数初始化L
,最后调用IncreaseSize
函数将L
的大小增加5个单位。
总结
这篇文章到这里就结束了
谢谢大家的阅读!
如果觉得这篇博客对你有用的话,别忘记三连哦。
我是豌豆射手^,让我们我们下次再见