0
点赞
收藏
分享

微信扫一扫

utarray:C语言动态数组宏

utarray:C语言动态数组宏

一、简介

1.1 介绍

​utarray.h​​中包含了一组用于C语言结构的通用动态数组宏。使用起来非常简单,只需要将​​utarray.h​​拷贝到你的源码目录,并在你的程序中使用它:

#include "utarray.h"

动态数组支持数组的基本操作,例如:push、pop和erase。数组的元素可以是任何基本数据类型或结构体类型,数组的操作类似于C++中STL vector的方法。
动态数组内部包含一块连续的内存区域,数组元素会拷贝到该区域,并通过realloc方法扩展以容纳更多push到里面的数据。

1.2 源码获取

​utarray.h​​源码可以在​​github​​上直接获取到(src/utarray.h):https://github.com/troydhanson/uthash

对应的国内​​gitee​​仓库:https://gitee.com/mirrors/uthash

二、使用方法

2.1 声明

数组的数据类型是​​UT_array​​,与其存储的数据类型无关,通过如下方式声明

UT_array *nums;

2.2 创建和释放

调用​​utarray_new​​来创建array,使用完数组之后,调用​​utarray_free​​来释放数组包括其内部所有元素。

2.3 Push、Pop等

​utarray.h​​核心功能是包含了元素的操作:​​push​​、​​pop​​和​​iterate​​操作,可以通过多种操作来一次处理一个元素或者多个元素,下面的示例中,仅使用push来插入元素。

三、元素

使用整型或字符串类型作为元素,是动态array最简单的例子。

3.1 整型

下面示例创建了一个整型​​utarray​​,push数字0 ~ 9到其中,然后打印它们,最后释放该数组。

#include <stdio.h>
#include "utarray.h"

int main(){
UT_array *nums;
int i, *p;

utarray_new(nums, &ut_int_icd);
for(i=0; i < 10; i++) utarray_push_back(nums, &i);

for(p=(int*)utarray_front(nums);
p!=NULL;
p=(int*)utarray_next(nums, p)) {
printf("%d\n",*p);
}

utarray_free(nums);
return 0;
}

注意:​utarray_push_back​​的第二个参数必须是指向元素类型的指针,对于整型,它的类型是​​int*​​。

3.2 字符串

下面示例创建了一个字符串​​utarray​​,push两个字符串到其中,然后打印它们,最后释放该数组。

#include <stdio.h>
#include "utarray.h"

int main() {
UT_array *strs;
char *s, <strong>p;

utarray_new(strs, &ut_str_icd);

s = "hello"; utarray_push_back(strs, &s);
s = "world"; utarray_push_back(strs, &s);
p = NULL;
while ((p=(char</strong>)utarray_next(strs, p))) {
printf("%s\n",*p);
}

utarray_free(strs);
return 0;
}

本例中,元素类型是​​char*​​,因此我们传入参数类型为​​char​。注意:**push操作生成一个字符串的副本并将其拷贝到数组中。

3.3 关于UT_icd

​utarray​​不仅仅支持整型和字符串(使用预定义的​​ut_int_icd​​和​​ut_str_icd​​),还支持其他任何类型的元素。你需要定义一个​​UT_icd​​辅助结构体,它包含​​utarray​​用到的初始化、拷贝和销毁元素操作。

typedef struct {
size_t sz;
init_f *init;
ctor_f *copy;
dtor_f *dtor;
}

其中​​init​​、​​copy​​和​​dtor​​函数指针的原型如下:

typedef void (ctor_f)(void *dst, const void *src);  /* 拷贝函数 */
typedef void (dtor_f)(void *elt); /* 销毁函数 */
typedef void (init_f)(void *elt); /* 初始化函数 */

​UT_icd​​辅助结构体的成员说明如下:

  • sz:是需要保存到​​utarray​​的元素的大小;
  • init:该函数在​​utarray​​初始化一个元素时使用,在​​utarray_resize​​或​​utarray_extend_back​​操作时调用。如果​​init​​指针为NULL,默认使用​​memset​​将所有元素设置为0;
  • copy:该函数在复制元素到​​utarray​​时使用,在​​utarray_push_back​​、​​utarray_insert​​、​​utarray_inserta​​和​​utarray_concat​​操作时调用。如果​​copy​​指针为NULL,默认用​​memcpy​​按位进行拷贝;
  • dtor:该函数在元素从​​utarray​​中移除时使用,在​​utarray_resize​​、​​utarray_pop_back​​、​​utarray_erase​​、​​utarray_clear​​、​​utarray_done​​和​​utarray_free​​操作时调用;如果元素在销毁时不需要释放资源,则​​dtor​​可以设置为NULL。
3.3.1 标准数据类型

下面的示例使用​​UT_icd​​及其默认方法来创建一个元素类型为long的​​utarray​​,例子中push两个long数字到其中,然后打印它们,最后释放数组。

/* long elements */
#include <stdio.h>
#include "utarray.h"

UT_icd long_icd = {sizeof(long), NULL, NULL, NULL };

int main() {
UT_array *nums;
long l, *p;
utarray_new(nums, &long_icd);

l=1; utarray_push_back(nums, &l);
l=2; utarray_push_back(nums, &l);

p=NULL;
while( (p=(long*)utarray_next(nums,p))) printf("%ld\n", *p);

utarray_free(nums);
return 0;
}
3.3.2 结构体

结构体也可以作为​​utarray​​的元素。如果结构体不需要特别的初始化、拷贝和销毁处理操作,我们可以使用​​UT_icd​​及其默认方法,下面的例子展示了包含两个整型的结构体,在这里将push两个数值,然后打印它们,最后释放数组。

1)结构体(简单)

/* structure type */
#include <stdio.h>
#include "utarray.h"

typedef struct {
int a;
int b;
} intpair_t;

UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL};

int main() {

UT_array *pairs;
intpair_t ip, *p;
utarray_new(pairs,&intpair_icd);

ip.a=1; ip.b=2; utarray_push_back(pairs, &ip);
ip.a=10; ip.b=20; utarray_push_back(pairs, &ip);

for(p=(intpair_t*)utarray_front(pairs);
p!=NULL;
p=(intpair_t*)utarray_next(pairs,p)) {
printf("%d %d\n", p->a, p->b);
}

utarray_free(pairs);
return 0;
}

如果结构体需要有特殊的初始化、拷贝和销毁操作时,​​UT_icd​​的作用就体现出来了。比如,当我们的结构体包含一个指针指向另外一块内存区域的时候,在结构体拷贝时做相应的内存拷贝(在结构体释放时做相关的释放),我们可以自定义​​UT_icd​​中对应的​​init​​、​​copy​​和​​dtor​​方法。
这里用到了两个概念:

  • 浅拷贝:只是拷贝结构体中的内容。
  • 深拷贝:将结构体与结构体指针成员指向的所有内存全部拷贝。

下面是一个实现深拷贝的示例:定义了一个整型和字符串类型的结构体类型数据,初始化时分配字符串内容,拷贝的时候拷贝字符串内容,释放的时候也要释放字符串的内容。

2)结构体(复杂)

#include <stdio.h>
#include <stdlib.h>
#include "utarray.h"

typedef struct {
int a;
char *s;
} intchar_t;

void intchar_copy(void *_dst, const void *_src) {
intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src;
dst->a = src->a;
dst->s = src->s ? strdup(src->s) : NULL;
}

void intchar_dtor(void *_elt) {
intchar_t *elt = (intchar_t*)_elt;
if (elt->s) free(elt->s);
}

UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor};

int main() {
UT_array *intchars;
intchar_t ic, *p;
utarray_new(intchars, &intchar_icd);

ic.a=1; ic.s="hello"; utarray_push_back(intchars, &ic);
ic.a=2; ic.s="world"; utarray_push_back(intchars, &ic);

p=NULL;
while( (p=(intchar_t*)utarray_next(intchars,p))) {
printf("%d %s\n", p->a, (p->s ? p->s : "null"));
}

utarray_free(intchars);
return 0;
}

四、引用

下表列出了​​utarray​​常用的操作宏,所有方法类似于C++的​​vector​​类。

OPERATIONS

DESCRIPTIONS

utarray_new(UT_array *a, UT_icd *icd)

allocate a new array

utarray_free(UT_array *a)

free an allocated array

utarray_init(UT_array *a,UT_icd *icd)

init an array (non-alloc)

utarray_done(UT_array *a)

dispose of an array (non-allocd)

utarray_reserve(UT_array *a,int n)

ensure space available for 'n' more elements

utarray_push_back(UT_array *a,void *p)

push element p onto a

utarray_pop_back(UT_array *a)

pop last element from a

utarray_extend_back(UT_array *a)

push empty element onto a

utarray_len(UT_array *a)

get length of a

utarray_eltptr(UT_array *a,int j)

get pointer of element from index

utarray_eltidx(UT_array *a,void *e)

get index of element from pointer

utarray_insert(UT_array *a,void *p, int j)

insert element p to index j

utarray_inserta(UT_array *a,UT_array *w, int j)

insert array w into array a at index j

utarray_resize(UT_array *dst,int num)

extend or shrink array to num elements

utarray_concat(UT_array *dst,UT_array *src)

copy src to end of dst array

utarray_erase(UT_array *a,int pos,int len)

remove len elements from a[pos]..a[pos+len-1]

utarray_clear(UT_array *a)

clear all elements from a, setting its length to zero

utarray_sort(UT_array *a,cmpfcn *cmp)

sort elements of a using comparison function

utarray_find(UT_array *a,void *v, cmpfcn *cmp)

find element v in utarray (must be sorted)

utarray_front(UT_array *a)

get first element of a

utarray_next(UT_array *a,void *e)

get element of a following e (front if e is NULL)

utarray_prev(UT_array *a,void *e)

get element of a before e (back if e is NULL)

utarray_back(UT_array *a)

get last element of a

五、注意事项

  1. ​utarray_new​​和​​utarray_free​​用于分配和释放一个​​utarray​​,而​​utarray_init​​和​​utarray_done​​是用在​​utarray​​已经分配之后,用于初始化和释放数组内部结构的资源。
  2. ​utarray_reserve​​用于预留增量个数的元素空间(不是​​utarray​​的所有容量),这点不同于C++ STL的"reserve"概念。
  3. ​utarray_sort​​需要一个类似于strcmp的比较函数,它接收两个参数a和b,在a大于b是返回正数,在a等于b时返回0,在a小于b时返回负数。下面是一个比较函数的例子:
int intsort(const void *a, const void *b) {
int _a = *(const int *)a;
int _b = *(const int *)b;
return (_a < _b) ? -1 : (_a > _b);
}
  1. ​utarray_find​​依据提供的比较函数,使用二分查找(bsearch)特定的元素。​​utarray​​在查找之前必须先使用相同的比较函数完成排序(utarray_sort使用快速qsort完成排序)。下面是一个字符串​​utarray​​示例:
#include <stdio.h>
#include "utarray.h"

static int strsort(const void *_a, const void *_b)
{
const char *a = *(const char* const *)_a;
const char *b = *(const char* const *)_b;
return strcmp(a,b);
}

int main()
{
UT_array *strs;
const char *s, <strong>p;

utarray_new(strs,&ut_str_icd);

s = "hello"; utarray_push_back(strs, &s);
s = "world"; utarray_push_back(strs, &s);
s = "one"; utarray_push_back(strs, &s);
s = "two"; utarray_push_back(strs, &s);
s = "three"; utarray_push_back(strs, &s);

p = NULL;
while ( (p=(const char</strong>)utarray_next(strs,p)) != NULL ) {
s = *p;
printf("%s\n",s);
}

printf("sorting\n");
utarray_sort(strs,strsort);

p = NULL;
while ( (p=(const char**)utarray_next(strs,p)) != NULL ) {
s = *p;
printf("finding %s\n",s);
p = utarray_find(strs,&s,strsort);
printf(" %s\n", (p != NULL) ? (*p) : "failed");
}

utarray_free(strs);
return 0;
}
  1. 指向特定元素的指针(通过调用​​utarray_eltptr​​,​​utarray_front​​,​​utarray_next​​,​​utarray_prev​​,​​utarray_back​​来获得) 会在有任何元素插入后失效,因为内部内存管理realloc可能会将元素重新指向到新的地址,出于这个原因,最好在代码中通过整数索引​​utarray_eltptr​​来引用元素。
#include <stdio.h>
#include "utarray.h"

int main(){
UT_array *nums;
int i, *p;

utarray_new(nums, &ut_int_icd);
for(i=0; i < 10; i++) utarray_push_back(nums, &i);

for(i = 0; i < utarray_len(nums); i++) {
printf("%d\n",*(utarray_eltptr(nums, i)));
}

utarray_free(nums);
return 0;
}
  1. 如果要覆盖默认的内存不足的处理行为(调用exit(-1)),请在包含​​utarray.h​​之前需改​​utarray_oom()​​宏,例如:
#define utarray_oom() do { longjmp(error_handling_location); } while (0)
#include "utarray.h"
  1. 如果我们要求插入数组的元素次序不能变,就不能使用​​utarray_find()​​宏。而要采用遍历的查找方式:
/**
* utarray_find_no_sort()
* lookup elements in array by sequence and as a replacement of utarray_find().
*
* DO NOT use utarray_find() since array must be sorted by calling utarray_sort()
* before calling utarray_find().
*/
static void* utarray_find_no_sort(UT_array *objects, void *eltpos, int(*cmpfn)(const void *elt, const char *findob), const void *findob)
{
void *elt = eltpos;
while ((elt = utarray_next(objects, elt)) != NULL) {
if (!cmpfn(elt, findob)) {
break;
}
}
return elt == eltpos? NULL : elt;
}


举报

相关推荐

0 条评论