![948128bead687004f4eeb09b38ec6e4c.png](https://i-blog.csdnimg.cn/blog_migrate/0f81a8879986e08c05bd0032846243fe.jpeg)
对于习惯使用高级语言编程的人来说,使用 C 语言编程最头痛的问题之一就是在使用数组需要事先确定数组长度。
C 语言本身不提供动态数组这种数据结构,本文将演示如何在 C 语言编程中实现一种对象来作为动态数组。
/* Author: iFantastic@cnblogs */
基本的 C 数组
C 语言编程中声明一个基本数组如下:
int
以上代码做了两件事:
● 在栈区开辟内存空间。准确说来是在函数 main 的栈区空间开辟一个 3000 * sizeof(int) 个字节的内存空间。通过这种方式开辟的内存空间会在程序运行到当前区块终点时(对本例而言就是 main 函数的底部)被自动释放掉。
● 创建一个指针指向新开辟的内存区域,并将该指针赋给变量 my_array 保存。我们可以通过下标的方式来访问数组里的成员,例如 my_array[271] 可以访问到第 272 个成员。你也可以通过另一种方式来访问数组里的成员,即 *(my_array + 271)。
由此可以看出,C 语言的数组实质就是内存管理操作,下标索引只是一种语法糖。
C 语言的数组有两个雾区:
● 很难随着数据的增加自动扩大数组。事实是你可以使用 realloc 函数扩大开辟在堆区的数组大小,当然我们想要的是能自动调整大小的数组对象。
● 你可以索引到数组边界以外的区域。由于在 C 语言并不检查数组的边界,也就是说你的确可以访问数组边界以外区域的内存地址,例如 my_array[5000] 语法上是可行的。因为下标索引只是一种语法糖,它实际上所做的是从指针 my_array 开始向后移动 5000 次并读取它停在的那个内存地址所保存的数据。当你索引数据边界以外区域时相当于读取尚未分配的内存上的内容,但这不是你真的想要的,并且可能带来潜在的严重后果。
如果我们可以忍受一些速度和内存空间上的牺牲,那么我们可以通过实现某种数据结构作为所谓的 “动态数组”。本文我们将这种数据结构称为 Vector,但这种数据结构不能解决我们在操作数集时遇到的所有问题,它适合于向其中追加成员,但不适合做插入和删除操作,如果你需要大量的插入和删除操作,链表这种数据结构更能符合你的需求,但链表也有它的问题,我们就不在这里做过多讨论。
定义 Vector 对象
本文我们将创建一个容纳整数的 “动态数组”,让我们将这种数据结构命名为 Vector。首先我们使用一个头文件 vector.h 来定义数据结构 Vector:
// 首先定义一个常量,该常量表示 Vector 内部一个数组对象的初始大小。
实现 Vector 对象
以下代码(vector.c)展示如何实现 Vector 数据结构:
#include
使用 Vector 对象
以下代码(vector-usage.c)展示如何使用 Vector 对象:
#include
以上代码我们使用 Vector 这种数据结构来作为一个动态数组,一开始 Vector 大小(size)为 100 个整数容量,后来我们添加了 150 个整数,再后来我们又在第 251 个位置添加一个整数 99999。编译并运行以上代码:
$
可以看到这个动态数组大小为 251 个整数容量(实际可以保存 400 个整数),第 28 个位置值为 173,中间一段位置使用了 0 填充,第 251 个位置值为 99999。
数据结构中的平衡艺术
本文展示了如何实现一种底层数据结构,通过理解底层的实现过程,你可以更好的理解一些高级语言的行为以及为什么它们会有某些速度瓶颈。
调整本文中的数据结构 Vector 内部的数组大小是一种开销很大的操作,因为它需要调用 realloc() 函数。realloc() 函数会调整指针指向的那片内存空间的大小,并返回一个指向调整后内存空间的指针。如果当前内存区域没有足够的剩余空间来扩展当前的内存空间,那么 realloc() 会开辟一片新的内存区域,并且将指针指向的旧内存空间内容复制到新的内存空间,然后释放旧的内存空间,然后返回新的内存空间指针。
所以如果我们遇到当前内存区域不够扩展我们的数组时,我们不得不进行开销很大的复制操作。为了减少这种情况出现的可能性,我们每次扩展内存空间时总是翻倍地开辟新的内存空间,这种策略带来的副作用就是可能会造成内存空间的浪费,这就是一种根据内存空间与速度之间的平衡。
另外本文实现的数据结构只能保存整数类型对象。如果我们数据结构中使用的数组保存指向空对象的指针而不是整数,那么我们就可以保存任意类型的值。但这样的话,每次我们读取该数据结构保存的数据时,都要遭遇解指针所带来的瓶颈,这就是另一种灵活度与性能之间的平衡。