このページの内容

v2.4.0, June 2026

はじめに

Cの構造体向けの汎用的な動的配列マクロが、uthashの utarray.h に含まれています。自分のCプログラムで使うには、utarray.h をソースディレクトリにコピーし、プログラムで使用するだけです。

#include "utarray.h"

動的配列は、配列要素のpush、pop、eraseなどの基本操作をサポートします。配列要素には、任意の単純なデータ型や構造体を使えます。配列の操作は、C++ STLのvectorのメソッドを大まかな基にしています。

動的配列の内部には連続したメモリー領域があり、そこに要素がコピーされます。このバッファーは、pushされたすべてのデータを格納できるよう、必要に応じて realloc で拡張されます。

ダウンロード

ヘッダーファイル utarray.h をダウンロードするには、https://github.com/troydhanson/uthash のリンクからuthashをクローンするかZIPファイルを取得し、src/ サブディレクトリを確認してください。

BSDライセンス

このソフトウェアは修正版BSDライセンスで提供されています。自由に利用できるオープンソースソフトウェアです。

プラットフォーム

utarray マクロは、次の環境でテストされています。

  • Linux,

  • Mac OS X,

  • Windows(Visual Studio 2008およびVisual Studio 2010を使用)

使い方

宣言

格納する要素の型にかかわらず、配列自体のデータ型は UT_array です。次のように宣言します。

UT_array *nums;

作成と解放

次に、utarray_new で配列を作成します。使用を終えたら、utarray_free で配列とそのすべての要素を解放します。

push、popなどの操作

utarrayの主な機能は、要素の格納、取り出し、それらに対する反復処理です。1つの要素または一定範囲の要素を一度に扱う操作がいくつかあり、選んで使えます。以下の例では、要素の挿入にpush操作だけを使います。

要素

整数や文字列の動的配列は、特に簡単に扱えます。例で示すのが分かりやすいでしょう。

整数

この例は、整数のutarrayを作成して0〜9をpushし、内容を表示します。最後に配列を解放します。

整数の要素

#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 の第2引数は、常に要素の型へのポインターです(そのため、リテラルは使えません)。整数の場合は int* になります。

文字列

この例は、文字列のutarrayを作成して2つの文字列をpushし、内容を表示してから解放します。

文字列の要素

#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**)を utarray_push_back の第2引数に渡します。「push」は元の文字列をコピーし、そのコピーを配列にpushすることに注意してください。

UT_icdについて

配列の要素には、整数や文字列だけでなく、任意の型を使えます。基本型や構造体を要素にできます。整数や文字列(定義済みの ut_int_icd と ut_str_icd を使用)を扱う場合を除き、補助構造体 UT_icd を定義する必要があります。この構造体には、utarrayが要素を初期化、コピー、破棄するために必要な情報がすべて含まれます。

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

3つの関数ポインター 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 は、配列に格納する要素のサイズです。

init 関数は、utarrayが空の要素を初期化する必要があるたびに呼び出されます。これは utarray_resize または utarray_extend_back に伴ってのみ発生します。init が NULL の場合は、既定でmemsetを使って新しい要素をゼロで埋めます。

copy 関数は、要素を配列にコピーするたびに使われます。utarray_push_back、utarray_insert、utarray_inserta、utarray_concat の実行中に呼び出されます。copy が NULL の場合は、既定でmemcpyによるビット単位のコピーを行います。

dtor 関数は、配列から取り除く要素の後処理に使われます。utarray_resize、utarray_pop_back、utarray_erase、utarray_clear、utarray_done、utarray_free によって呼び出されることがあります。破棄時の後処理が不要な要素なら、dtor を NULL にできます。

スカラー型

次の例は、すべて既定の設定の UT_icd を使って、long 要素のutarrayを作成します。2つのlong値をpushし、それらを表示してから配列を解放します。

longの要素

#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;
}

構造体

utarrayの要素には構造体を使えます。初期化、コピー、破棄に特別な処理が不要な構造体なら、すべて既定の設定の UT_icd を使えます。この例は、2つの整数からなる構造体です。2つの値をpushし、それらを表示してから配列を解放します。

構造体(単純な例)

#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;
}

utarrayの要素が、初期化、コピー、破棄に特別な処理を必要とする構造体である場合に、UT_icd の真価が分かります。

たとえば、構造体のコピー時にコピーが必要で、構造体の解放時に解放が必要な関連メモリー領域へのポインターを構造体が含む場合、独自の init、copy、dtor メンバーを UT_icd に指定できます。

ここでは、整数と文字列を含む構造体を例にします。要素をコピーする際(たとえば配列へのpush時)には、s ポインターの参照先を「深いコピー」にしたいと考えます(元の要素と新しい要素が、それぞれ独立した s のコピーを指すようにするためです)。要素を破棄する際には、その s のコピーも「深い解放」を行います。この例は、s の値が NULL でも動作するように書かれています。

構造体(複雑な例)

#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クラスを大まかな基にしています。

操作

utarray_new(UT_array *a, UT_icd *icd)

新しい配列を割り当てる

utarray_free(UT_array *a)

割り当て済みの配列を解放する

utarray_init(UT_array *a,UT_icd *icd)

配列を初期化する(構造体自体は割り当てない)

utarray_done(UT_array *a)

配列の内部リソースを解放する(構造体自体は解放しない)

utarray_reserve(UT_array *a,int n)

さらにn個の要素を格納できる空きを確保する

utarray_push_back(UT_array *a,void *p)

要素pをaにpushする

utarray_pop_back(UT_array *a)

aの最後の要素をpopする

utarray_extend_back(UT_array *a)

aに空の要素をpushする

utarray_len(UT_array *a)

aの長さを取得する

utarray_eltptr(UT_array *a,int j)

インデックスから要素のポインターを取得する

utarray_eltidx(UT_array *a,void *e)

ポインターから要素のインデックスを取得する

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

要素pをインデックスjに挿入する

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

インデックスjの要素をpで置き換える

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

配列wを配列aのインデックスjに挿入する

utarray_resize(UT_array *dst,int num)

配列をnum個の要素に拡張または縮小する

utarray_concat(UT_array *dst,UT_array *src)

srcを配列dstの末尾にコピーする

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

a[pos]..a[pos+len-1]の範囲にあるlen個の要素を削除する

utarray_clear(UT_array *a)

aのすべての要素を消去して長さをゼロにする

utarray_sort(UT_array *a,cmpfcn *cmp)

比較関数を使ってaの要素をソートする

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

utarray内の要素vを検索する(ソート済みであることが必須)

utarray_front(UT_array *a)

aの最初の要素を取得する

utarray_next(UT_array *a,void *e)

aの要素eの次の要素を取得する(eがNULLなら先頭要素)

utarray_prev(UT_array *a,void *e)

aの要素eの前の要素を取得する(eがNULLなら末尾要素)

utarray_back(UT_array *a)

aの最後の要素を取得する

注意事項

  1. utarray_new と utarray_free は、新しい配列の割り当てと解放に使います。一方、UT_arrayがすでに割り当て済みで、初期化や内部リソースの解放だけが必要な場合は、utarray_init と utarray_done を使えます。

  2. utarray_reserve が受け取るのは、確保する要素数の「増分」であり、配列に求める総容量ではありません。この点はC++ STLの「reserve」の考え方と異なります。

  3. utarray_sort は、通常の strcmp と同じ規約の比較関数を受け取ります。この関数は2つの要素(aとb)を受け取り、aがbより前なら負の値、ソート上でaとbが等しければ0、bがaより前なら正の値を返します。比較関数の例を示します。

    int intsort(const void *a, const void *b) {
        int _a = *(const int *)a;
        int _b = *(const int *)b;
        return (_a < _b) ? -1 : (_a > _b);
    }
  4. utarray_find は、与えられた比較関数に従い、指定した値を持つ要素を二分探索で見つけます。utarrayは、先に同じ比較関数を使ってソートしておく必要があります。文字列のutarrayで utarray_find を使う例は、tests/test61.c に含まれています。

  5. 特定の要素へのポインター(utarray_eltptr、utarray_front、utarray_next、utarray_prev、utarray_back で取得)は、utarrayに別の要素が挿入されるたびに無効になります。内部のメモリー管理で、要素の格納領域を新しいアドレスへ realloc する必要が生じ得るためです。そのため、実行中に要素の挿入が起こり得るコードでは、通常、整数のインデックスで要素を参照する方がよいでしょう。

  6. メモリー不足時の既定の処理(exit(-1) を呼び出す動作)を変更するには、utarray_oom() マクロを utarray.h のインクルード前に定義し直してください。たとえば、次のようにします。

    #define utarray_oom() do { longjmp(error_handling_location); } while (0)
    ...
    #include "utarray.h"