v2.4.0, June 2026
GitHubのプロジェクトページはこちらです。
はじめに
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クラスを大まかな基にしています。
操作
|
新しい配列を割り当てる |
|
割り当て済みの配列を解放する |
|
配列を初期化する(構造体自体は割り当てない) |
|
配列の内部リソースを解放する(構造体自体は解放しない) |
|
さらにn個の要素を格納できる空きを確保する |
|
要素pをaにpushする |
|
aの最後の要素をpopする |
|
aに空の要素をpushする |
|
aの長さを取得する |
|
インデックスから要素のポインターを取得する |
|
ポインターから要素のインデックスを取得する |
|
要素pをインデックスjに挿入する |
|
インデックスjの要素をpで置き換える |
|
配列wを配列aのインデックスjに挿入する |
|
配列をnum個の要素に拡張または縮小する |
|
srcを配列dstの末尾にコピーする |
|
a[pos]..a[pos+len-1]の範囲にあるlen個の要素を削除する |
|
aのすべての要素を消去して長さをゼロにする |
|
比較関数を使ってaの要素をソートする |
|
utarray内の要素vを検索する(ソート済みであることが必須) |
|
aの最初の要素を取得する |
|
aの要素eの次の要素を取得する(eがNULLなら先頭要素) |
|
aの要素eの前の要素を取得する(eがNULLなら末尾要素) |
|
aの最後の要素を取得する |
注意事項
-
utarray_newとutarray_freeは、新しい配列の割り当てと解放に使います。一方、UT_arrayがすでに割り当て済みで、初期化や内部リソースの解放だけが必要な場合は、utarray_initとutarray_doneを使えます。 -
utarray_reserveが受け取るのは、確保する要素数の「増分」であり、配列に求める総容量ではありません。この点はC++ STLの「reserve」の考え方と異なります。 -
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); } -
utarray_findは、与えられた比較関数に従い、指定した値を持つ要素を二分探索で見つけます。utarrayは、先に同じ比較関数を使ってソートしておく必要があります。文字列のutarrayでutarray_findを使う例は、tests/test61.cに含まれています。 -
特定の要素へのポインター(
utarray_eltptr、utarray_front、utarray_next、utarray_prev、utarray_backで取得)は、utarrayに別の要素が挿入されるたびに無効になります。内部のメモリー管理で、要素の格納領域を新しいアドレスへreallocする必要が生じ得るためです。そのため、実行中に要素の挿入が起こり得るコードでは、通常、整数のインデックスで要素を参照する方がよいでしょう。 -
メモリー不足時の既定の処理(
exit(-1)を呼び出す動作)を変更するには、utarray_oom()マクロをutarray.hのインクルード前に定義し直してください。たとえば、次のようにします。#define utarray_oom() do { longjmp(error_handling_location); } while (0) ... #include "utarray.h"