An array (vector) is a common-place data type, used to hold and describe a collection of elements. These elements can be fetched at runtime by one or more indices (identifying keys). A distinguishing feature of an array compared to a list is that they allow for constant-time random access lookup, compared to the latters sequential access. Resizable arrays allow for an unspecified upper-bound of collection elements at runtime, and are conceptuality similar to a list. These dynamic arrays are more complicated and less used in introduction to its compatriot list, which is dynamic by nature. Using C as the language of implementation this post will guide you through building a simple vector data-structure. The structure will take advantage of a fixed-size array, with a counter invariant that keeps track of how many elements are currently present. If the underlying array becomes exhausted, the addition operation will re-allocate the contents to a larger size, by way of a copy.

The Make File

‘Make’ is a popular utility used throughout software development to build executable artifacts (programs and libraries) from described source code. Through a simple DSL, associations from descriptive short-names (targets) and a series of related commands to execute are made. Running the ‘make’ command executes the first present target, and this must be considered in the design of the file. Below is a sample Makefile which provides the vector project with simple build, debug and clean targets.

CC=gcc
CFLAGS=
RM=rm -rf
OUT=vector all: build build: main.o vector.o
$(CC) $(CFLAGS) -o $(OUT) main.c vector.c
$(RM) *.o debug: CFLAGS+=-DDEBUG_ON
debug: build main.o: main.c vector.h
$(CC) $(CFLAGS) -c main.c vector.o: vector.c vector.h
$(CC) $(CFLAGS) -c vector.c clean:
$(RM) *.o $(OUT)

Looking at the code example above you will notice a few variables which are used to define specific aspects used when running the targets (such as the compiler command and flags used). To keep things modular the compilation of the ‘main’ and ‘vector’ source-code files has been split, with file dependences specific to each target specified after the short-name. The ‘debug’ target appends a macro definition flag which is used to include any debug information present in the source code.

The Header File

Defining a header file allows the programmer to separate specific aspects of the programs source-code into reusable files. These files commonly contain forward declarations of identifiers and functions. This allows a user to include the codes header file in their own work, separating the definition from the implementation. Including a header file produces the same results as copying the full contents into the callers file. Below shows the header file implemented for the vector example.

#ifndef VECTOR_H
#define VECTOR_H #define VECTOR_INIT_CAPACITY 4 #define VECTOR_INIT(vec) vector vec; vector_init(&vec)
#define VECTOR_ADD(vec, item) vector_add(&vec, (void *) item)
#define VECTOR_SET(vec, id, item) vector_set(&vec, id, (void *) item)
#define VECTOR_GET(vec, type, id) (type) vector_get(&vec, id)
#define VECTOR_DELETE(vec, id) vector_delete(&vec, id)
#define VECTOR_TOTAL(vec) vector_total(&vec)
#define VECTOR_FREE(vec) vector_free(&vec) typedef struct vector {
void **items;
int capacity;
int total;
} vector; void vector_init(vector *);
int vector_total(vector *);
static void vector_resize(vector *, int);
void vector_add(vector *, void *);
void vector_set(vector *, int, void *);
void *vector_get(vector *, int);
void vector_delete(vector *, int);
void vector_free(vector *); #endif

We wrap the contents of this file in a definition condition to make sure that even with multiple inclusion between aggregate source code files, only one inclusion is processed in the result. A ‘vector’ type definition is included which provides access to the capacity and total current elements in the collection. Along with this, a ‘items’ variable with a pointer of void pointers is included, allowing us to insert a heterogeneous collection of elements into the vector. The ‘vector_resize’ method is defined to be ‘static’ resulting in successful execution of the function only occurring in the file it is defined in (accessibility control).

The Implementation File

Using the header file definition, the following file is used to implement these methods. As discussed in the previous section ‘void pointers’ are used to reference the collection elements. Void pointers are pointers which point to some arbitrary data that has no specific type. As a consequence you are unable to directly deference a pointer of this type and must first provide a casting type.

#include <stdio.h>
#include <stdlib.h> #include "vector.h" void vector_init(vector *v)
{
v->capacity = VECTOR_INIT_CAPACITY;
v->total = ;
v->items = malloc(sizeof(void *) * v->capacity);
} int vector_total(vector *v)
{
return v->total;
} static void vector_resize(vector *v, int capacity)
{
#ifdef DEBUG_ON
printf("vector_resize: %d to %d\n", v->capacity, capacity);
#endif void **items = realloc(v->items, sizeof(void *) * capacity);
if (items) {
v->items = items;
v->capacity = capacity;
}
} void vector_add(vector *v, void *item)
{
if (v->capacity == v->total)
vector_resize(v, v->capacity * );
v->items[v->total++] = item;
} void vector_set(vector *v, int index, void *item)
{
if (index >= && index < v->total)
v->items[index] = item;
} void *vector_get(vector *v, int index)
{
if (index >= && index < v->total)
return v->items[index];
return NULL;
} void vector_delete(vector *v, int index)
{
if (index < || index >= v->total)
return; v->items[index] = NULL; for (int i = index; i < v->total - ; i++) {
v->items[i] = v->items[i + ];
v->items[i + ] = NULL;
} v->total--; if (v->total > && v->total == v->capacity / )
vector_resize(v, v->capacity / );
} void vector_free(vector *v)
{
free(v->items);
}

Looking at the code example above you will notice that the ‘vector_resize’ function is called if certain conditions are met on addition or deletion. If the current vector capacity has been exhausted when an addition has been requested the size is doubled and the vector contents re-allocated. In a similar fashion, upon deletion, if the vector is a quarter full the contents is reallocated to a vector of half the current size. These conditions for resizing work well in practice to balance memory capacity and computation time required to fulfill each resize.

The Test Case

With all the pieces put in place we are now able to test case the implementation. Below shows an example using the direct functions, adding a few strings (character sequences) to a collection, printing the contents, modifying the contents and then printing it out again. One unfortunate use-case detail that can not be avoided with the use of void pointers is the necessary cast.

#include <stdio.h>
#include <stdlib.h> #include "vector.h" int main(void)
{
int i; vector v;
vector_init(&v); vector_add(&v, "Bonjour");
vector_add(&v, "tout");
vector_add(&v, "le");
vector_add(&v, "monde"); for (i = ; i < vector_total(&v); i++)
printf("%s ", (char *) vector_get(&v, i));
printf("\n"); vector_delete(&v, );
vector_delete(&v, );
vector_delete(&v, ); vector_set(&v, , "Hello");
vector_add(&v, "World"); for (i = ; i < vector_total(&v); i++)
printf("%s ", (char *) vector_get(&v, i));
printf("\n"); vector_free(&v);
}

To simplify the use of the vector implementation the header file defines a few macro functions which can be used in place of the base function calls. Below highlights these definition in practice, removing some of the verbosity present in the previous example.

#include <stdio.h>
#include <stdlib.h> #include "vector.h" int main(void)
{
int i; VECTOR_INIT(v); VECTOR_ADD(v, "Bonjour");
VECTOR_ADD(v, "tout");
VECTOR_ADD(v, "le");
VECTOR_ADD(v, "monde"); for (i = ; i < VECTOR_TOTAL(v); i++)
printf("%s ", VECTOR_GET(v, char*, i));
printf("\n"); VECTOR_DELETE(v, );
VECTOR_DELETE(v, );
VECTOR_DELETE(v, ); VECTOR_SET(v, , "Hello");
VECTOR_ADD(v, "World"); for (i = ; i < VECTOR_TOTAL(v); i++)
printf("%s ", VECTOR_GET(v, char*, i));
printf("\n"); VECTOR_FREE(v);
}

Despite still having to provide a casting data type when retrieving a collection element, the macros clean-up and simplify the process a great deal.

Resources

Implementing a Dynamic Vector (Array) in C(使用c实现动态数组Vector)的更多相关文章

  1. 动态数组 - vector

    #include <iostream> #include <vector> // 头文件 using namespace std; int main() { vector< ...

  2. 【模板】c++动态数组vector

    相信大家都知道$C$++里有一个流弊的$STL$模板库.. 今天我们就要谈一谈这里面的一个容器:动态数组$vector$. $vector$实际上类似于$a[]$这个东西,也就是说它重载了$[]$运算 ...

  3. C++ STL之动态数组vector(⽮量)的使⽤

    写再最前面:摘录于柳神的笔记:   之前C语⾔⾥⾯⽤ int arr[] 定义数组,它的缺点是数组的⻓度不能随⼼所欲的改变,⽽C++⾥⾯有⼀个能完全替代数组的动态数组 vector (有的书⾥⾯把它翻 ...

  4. 越努力越幸运--动态数组vector

    最近回忆山哥写的stl,觉得很好用,也写了一份. 感谢群里的大佬帮忙review,还是很多的问题的. code:https://github.com/HellsingAshen/vector_c.gi ...

  5. C++ vector动态数组

    #include<vector>头文件 vector类称作向量类 百度百科的解释:https://baike.baidu.com/item/vector/3330482 我喜欢把知识点拿出 ...

  6. C++向量 vector动态数组

    需要包含头文件, #include  <vector>    using namespace std; vector 容器与数组相比其优点在于它能够根据需要随时自动调整自身的大小以便容下所 ...

  7. vector:动态数组

    vector是C++标准模板库中的部分内容,中文偶尔译作“容器”,但并不准确.它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库.vector之所以被认为是一个容器,是因为它能够像容器一样存 ...

  8. vector & array

    private static const NUM_LOOPS:int = 15; public function VectorTest():void { var vector:Vector.<i ...

  9. 数组Array和列表集合ArrayList、LinkedList和Vector的区别

    一.ArrayList和Vector的区别 ArrayList与Vector主要从以下方面来说. 1.同步性: Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同 ...

随机推荐

  1. 解决canvas图片getImageData,toDataURL跨域问题

    图片服务器需要配置Access-Control-Allow-Origin 当需要需要对canvas图片进行getImageData()或toDataURL()操作的时候,跨域问题就出来了.图片服务器需 ...

  2. bug的全部

    BUG 的生命周期 BUG 的生命周期 Bug-->软件程序的漏洞或缺陷 Bug 的类型:代码错误.设计缺陷.界面优化.性能问题.配置相关.安装部署.安全相关.标准规划.测试脚本....其他(功 ...

  3. mysql索引设计的注意事项

    mysql索引设计的注意事项 目录 一.索引的重要性 二.执行计划上的重要关注点 (1).全表扫描,检索行数 (2).key,using index(覆盖索引) (3).通过key_len确定究竟使用 ...

  4. 8张图,让你彻底理解三极管的开关功能 && 經典線路圖

    三极管除了可以当作交流信号放大器之外,也可以作为开关之用.严格说起来,三极管与一般的机械接点式开关在动作上并不完全相同,但是它却具有一些机械式开关所没有的特点. 为了很好的理解三极管的开关功能,下面以 ...

  5. java8 Date Localdatetime instant 相互转化(转) 及当天的最大/最小时间

    Java 8中 java.util.Date 类新增了两个方法,分别是from(Instant instant)和toInstant()方法 // Obtains an instance of Dat ...

  6. 1 request模块

    官方文档真是好用的一匹 官方文档:https://2.python-requests.org//zh_CN/latest/index.html 参考blog:https://www.cnblogs.c ...

  7. Libreoffice/Office:禁止首字母自动大写功能

     造冰箱的大熊猫@cnblogs 2019/1/24 在LibreOffice(5.1.6.2)中,要禁止或者使能首字母自动大写功能,点击菜单项“Tools>>AutoCorrect Op ...

  8. Java Web项目案例之---登录注册和增删改查(jsp+servlet)

    登录注册和增删改查(jsp+servlet) (一)功能介绍 1.用户输入正确的密码进行登录 2.新用户可以进行注册 3.登录后显示学生的信息表 4.可以添加学生 5.可以修改学生已有信息 6.可以删 ...

  9. Count the Buildings

    K - Count the Buildings 参考:Count the Buildings 思路可以借鉴,但是代码略有问题 写的时候 re 了 9 发,然后把变量定义的顺序换了一下居然 A 了,以为 ...

  10. HDU 5831 Rikka with Parenthesis II ——(括号匹配问题)

    用一个temp变量,每次出现左括号,+1,右括号,-1:用ans来记录出现的最小的值,很显然最终temp不等于0或者ans比-2小都是不可以的.-2是可以的,因为:“))((”可以把最左边的和最右边的 ...