C において配列へのポインタと配列長を 1 つの構造体にまとめる

C では (malloc 関数などによりメモリ領域を確保した場合の) 配列の長さを知る事は難しいと思います。 そのため、関数の引数として配列を渡す場合などに、初歩的な教科書ではよく配列へのポインタと同時に配列の長さを渡していると思います。

/* 配列長 */
size_t length = 5;
/* 配列へのポインタ */
int* arrayPt = (int*) malloc( sizeof(int) * length );

/* 別の関数に配列へのポインタを渡す際に, 同時に配列長も渡す事が多い */
function( arrayPt, length );

まあこの方法でもいいですけど、やはり 1 つにまとめた方がすっきりしますよね。 というわけで配列へのポインタと配列長を 1 つの構造体にまとめてしまいましょう。

3 次元配列の場合のサンプル

3 次元配列の場合のサンプルを以下に書いておきます。

#include <stdio.h>
#include <malloc.h>

/**
* 3 次元配列の実体と長さを 1 つにまとめた構造体
*/
typedef struct {
size_t length1; /* 1 軸方向の要素数 */
size_t length2; /* 2 軸方向の要素数 */
size_t length3; /* 3 軸方向の要素数 */
int*** e;
int** _e1;
int* _e2; /* 3 次元配列の実体 */
} ArrayInt3;
typedef ArrayInt3 *ArrayInt3Pt;

/**
* 構造体 ArrayInt3 用にメモリ確保を行う関数
* この関数を利用してメモリ確保した場合, プログラム終了前に delete_ArrayInt3 関数で
* メモリ領域の解放を行わなければいけない.
* @param size1 3 次元配列の 1 軸方向の要素数
* @param size2 3 次元配列の 2 軸方向の要素数
* @param size3 3 次元配列の 3 軸方向の要素数
* @return 確保したメモリ領域へのポインタ
*/
ArrayInt3Pt new_ArrayInt3( size_t size1, size_t size2, size_t size3 ) {
size_t i;
ArrayInt3Pt pt = (ArrayInt3Pt) malloc( sizeof(ArrayInt3) );
if ( (pt->_e2 = (int*) malloc( sizeof(int) * size1 * size2 * size3 )) == NULL ) {
return NULL;
}
if ( (pt->_e1 = (int**) malloc( sizeof(int*) * size1 * size2 )) == NULL ) {
free( pt->_e2 );
return NULL;
}
if ( (pt->e = (int***) malloc( sizeof(int**) * size1 )) == NULL ) {
free( pt->_e2 );
free( pt->_e1 );
return NULL;
}
for ( i = 0; i < size1; i++ ) {
pt->e[i] = pt->_e1 + i * size2;
}
for ( i = 0; i < size1 * size2; i++ ) {
pt->_e1[i] = pt->_e2 + i * size3;
}
pt->length1 = size1;
pt->length2 = size2;
pt->length3 = size3;
return pt;
}

/**
* 構造体 ArrayInt3 用確保したメモリ領域の解放を行う関数
* @param pt ArrayInt3 構造体用に確保したメモリ領域へのポインタ
* @return なし
*/
void delete_ArrayInt3( ArrayInt3Pt pt ) {
if ( pt != NULL ) {
if ( pt->_e2 != NULL ) { free( pt->_e2 ); pt->_e2 = NULL; }
if ( pt->_e1 != NULL ) { free( pt->_e1 ); pt->_e1 = NULL; }
if ( pt->e != NULL ) { free( pt->e ); pt->e = NULL; }
free( pt );
}
}

int main(void) {

/* 変数宣言 */
size_t i, j, k;
ArrayInt3Pt arrayPt = NULL;

/* 4x10x3 の 3 次元配列の生成 */
arrayPt = new_ArrayInt3(4,10,3);
if ( arrayPt == NULL ) { return -1; }

/* 多重 for 文にて配列にアクセスする */
for ( i = 0; i < arrayPt->length1; i++ ) {
for ( j = 0; j < arrayPt->length2; j++ ) {
for ( k = 0; k < arrayPt->length3; k++ ) {
arrayPt->e[i][j][k] = i * j * k;
printf( "%d ", arrayPt->e[i][j][k] );
}
puts("");
}
puts("");
}

/* メモリ領域解放 */
delete_ArrayInt3(arrayPt);
arrayPt = NULL;

/* 終了 */
return 0;

}

「配列へのポインタ」 という言葉について

一般的にどういう言い方をするのかよくわからなかったので 「配列へのポインタ」 と言っていますが、単に int 型用のメモリ領域の並びを配列だと思っているだけで、「配列」 という呼び方は正しくないかもしれません。 (上の例では) 実際にはただの int 型用のメモリ領域へのポインタです。