美文网首页Linux三方库读书工具癖
优秀开源库uthash之utarray.h

优秀开源库uthash之utarray.h

作者: konishi5202 | 来源:发表于2019-04-05 16:49 被阅读38次

    一、简介

    1.1 介绍

    utarray.h中包含了一组用于C结构体的通用array宏。使用起来非常简单,只需要将utarray.h拷贝到你的项目,并包含进你的源码即可:

    #include "utarray.h"
    

    动态array支持基本的array操作:push、pop和erase。array的元素可以是任何基本类型或者符合的结构体类型。

    动态array内部通过一个连续的内存区域来实现,这个内存区域将会根据push的数据内容,通过realloc方法增长。

    1.2 源码获取

    utarray.h的源码可以在GitHub上直接获取(src/utarray.h):

    https://github.com/troydhanson/uthash

    二、使用方法

    2.1 声明

    不管元素的数据类型怎样,utarray.h本身使用的数据类型是UT_array:

    UT_array *nums;
    

    2.2 new and free

    接下来是创建array,当我们使用完之后,还需要释放:

    utarray_new
    utarray_free
    

    utarray_free将会释放包括内部的所有元素。

    2.3 push and pop

    utarray.h核心的功能是提供了内部元素的:push、pop和iterate操作。

    三、元素

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

    3.1 整数

    下面的例子使用整数push了0 ~ 9到array,然后打印,最后free。

    #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的第二个参数必须是指向元素类型的指针。

    3.2 字符串

    下面实例创建了一个字符串array,然后push两个字符串,然后打印,最后free。

    #include <stdio.h>
    #include "utarray.h"
    
    int main() {
      UT_array *strs;
      char *s, **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**)utarray_next(strs,p))) {
        printf("%s\n",*p);
      }
    
      utarray_free(strs);
    
      return 0;
    }
    

    在本例中,由于元素类型是char *,因此我们传入的参数为char **。

    注意:push操作会导致源字符串到array的拷贝动作。

    3.3 关于UT_icd

    utarray.h不仅仅支持整数和字符串,还支持其他任何类型的元素。除了整形和字符串类型外,你需要定义一个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);
    
    • sz:是需要保存到array的元素的大小;
    • init:函数指针,该函数将会在utarray需要初始化一个空元素时被调用。这是utarray_resize或utarray_extend_back操作的副产品;如果init为NULL,那么元素的所有值将会默认用memset设置为0;
    • copy:函数指针,该函数用于元素被push进array时被调用,比如utarray_push_back、utarray_insert、utarray_inserta和utarray_concat;如果copy为NULL,将会默认默认用memcpy按位进行拷贝;
    • dtor:函数指针,该函数用于元素从array中移除时被调用,比如utarray_resize、utarray_pop_back、utarray_erase、utarray_clear、utarray_done和utarray_free;如果元素不需要释放资源,则dtor可以设为NULL。

    3.3.1 标准数据类型

    下面的例子,使用UT_icd的默认方法,来使用C语言的其他标准类型,比如这里是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的默认方法;但如果有自己的特殊操作,则需要自行定义对应的方法。

    /* 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中对应的init、copy和dtor方法了。

    这里用到了两个概念:

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

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

    #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 description
    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用于分配和释放一个array;然而utarray_init和utarray_down是在UT_array已经分配之后,用于初始化和释放内部结构的资源;
    2. utarray_reserve takes the "delta" of elements to reserve(not the total desired capacity of the array-- this differs from the C++ STL "reserve" notion);
    3. utarray_sort expects a comparison function having the usual strcmp -like convention where it accepts two elements (a and b) and returns a negative value if a precedes b, 0 if a and b sort equally, and positive if b precedes a. This is an example of a comparison function:
    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 uses a binary search to locate an element having a certain value according to the given comparison function. The utarray must be first sorted using the same comparison function. An example of using utarray_find with a utarray of strings is included in tests/test61.c.

    2. A pointer to a particular element (obtained using utarray_eltptr or utarray_front, utarray_next, utarray_prev, utarray_back) becomes invalid whenever another element is inserted into the utarray. This is because the internal memory management may need to realloc the element storage to a new address. For this reason, it’s usually better to refer to an element by its integer index in code whose duration may include element insertion.

    想了解更多有关嵌入式Linux驱动、应用、常用开源库、框架、模式、重构等相关相关的主题内容,请长安下面图片,关注我的公众号(只有技术干货分享,不拉稀摆带!)。

    image

    相关文章

      网友评论

        本文标题:优秀开源库uthash之utarray.h

        本文链接:https://www.haomeiwen.com/subject/lchkiqtx.html