Easy-to-Handle Variable Size Multi-Dimensional Arrays in C/C++

In C/C++, array sizes need to be known at compile time, since dimension bounds of build-in arrays must be specified by constant expressions. This is a problem for many applications where the array sizes are determined at run time.

Another consequence of restricting bounds to constant expressions is that you cannot declare a function parameter in such a way that arrays (with more than one dimension) of any size can be passed to it.

Example:

#include <stdio.h>

void printMatrix(int m[ ][3], int rowc)   // The number of columns (specified by
{                                         // the constant 3) cannot be omitted.
  /* ... */   m[i][j]   /* ... */
}

int main(void)
{
  int a1[2][3], a2[10][3], a3[6][10];
  // ...
  printMatrix(a1, 2);      // OK, prints matrix with 2 rows and 3 columns
  printMatrix(a2, 10);     // OK, prints matrix with 10 rows and 3 columns
  printMatrix(a3, 6);      // error: wrong number of columns
  // ...
}
The VarArray library presented here provides easy-to-use macros and functions to overcome these restrictions. The advantages of VarArrays are:

After replacing the build-in arrays with VarArrays, the example above will look like the following:

Example using VarArrays:

#include <stdio.h>
#include <var_array.h>
// Don't forget to link this program with the flag -lvar_array.

void printMatrix(vaArray_2d(int) m)
{
  int rowc = vaSize(m,1);     // number of rows
  int colc = vaSize(m,2);     // number of columns

  /* ... */   m[i][j]   /* ... */
}

int main(void)
{
  vaArray_2d(int) a1 = vaCreate_2d( 2,  3, int, NULL);
  vaArray_2d(int) a2 = vaCreate_2d(10,  3, int, NULL);
  vaArray_2d(int) a3 = vaCreate_2d( 6, 10, int, NULL);
  // ...
  printMatrix(a1);     // OK, prints matrix with 2 rows and 3 columns
  printMatrix(a2);     // OK, prints matrix with 10 rows and 3 columns
  printMatrix(a3);     // OK, prints matrix with 6 rows and 10 columns
  // ...
  vaDelete(a1);
  vaDelete(a2);
  vaDelete(a3);
  // ...
}

Array Types

The type of an VarArray includes its dimensionality (the number of its dimensions but not their size) and also the data type of the elements contained within the array. VarArray types are specified by macros that are defined in the header file <var_array.h>:

vaArray_1d(element_type)
vaArray_2d(element_type)
vaArray_3d(element_type)
vaArray_4d(element_type)
vaArray_5d(element_type)

Example:

vaArray_3d(double) c;   // c is an 3-dimensional array of doubles
Note that the statement above does not allocate any memory to contain the array elements. Memory space for an VarArray must be allocated by using the vaCreate_...() functions described in the section Creating Arrays.
Caution: Do not declare more than one VarArray per declarator list. This is necessary because each array type macro simply expands to text that specifies a pointer type.

For example, each of the following two lines (they are equivalent since vaArray_1d(int) expands to int *) declares only one VarArray, a, and a single scalar integer variable named b.

vaArray_1d(int) a, b;   // This is equivalent to the following line:
int * a, b;             // Note: b is a scalar variable of type "int" (not an VarArray)
Another way to deal with this restriction is to name array types via typedef:
typedef vaArray_1d(int) intArray_1d;
intArray_1d a, b;     // a and b are both VarArrays (1-dimensional arrays of integers)

Working with VarArrays

Creating Arrays

To allocate the storage for an VarArray, you have to call the appropriate vaCreate_...() function:

vaArray_1d(element_type) vaCreate_1d(  int dim1, // number of elements
element_type, const void *ptr_to_storage );

vaArray_2d(element_type) vaCreate_2d(  int dim1, // number of rows
int dim2, // number of columns
element_type, const void *ptr_to_storage );

vaArray_3d(element_type) vaCreate_3d(  int dim1, int dim2, int dim3,
element_type, const void *ptr_to_storage );

vaArray_4d(element_type) vaCreate_4d(  int dim1, int dim2, int dim3, int dim4,
element_type, const void *ptr_to_storage );

vaArray_5d(element_type) vaCreate_5d(  int dim1, int dim2, int dim3, int dim4, int dim5,
element_type, const void *ptr_to_storage );
Parameters:
dim... Specifies the length of the corresponding dimension of the array.
element_type Data type of the array elements.
ptr_to_storage Could be a pointer to a storage area large enough to hold all elements of the array. This is useful if you want to convert a build-in array to an VarArray (see Printing Arrays for a example). If a NULL pointer is passed, all memory for the VarArray will be allocated by the vaCreate_...() function.
Once you have assigned the return value of vaCreate_...() to a variable, you can then use that variable as your VarArray. To deallocate the storage, use vaDelete().

Accessing Elements

The elements in an VarArray are accessed by means of the index operator [ ] (just like elements in an regular C/C++ build-in array). The number of indices may be less than the number of dimensions of the indexed VarArray. In this case, the result is itself an VarArray containing all the dimensions which are not addressed by the indices.

Example:

vaArray_3d(int) cube;   // cube is an 3-dimensional array of integers
// ...
printmatrix(cube[0]);   // OK, cube[0] has type vaArray_2d(int)

Querying Array Bounds

int vaSize( VarArray, int dim );

The vaSize() function returns the length of the dimension specified by dim. For example, vaSize(m,1) returns the length of the first dimension of the VarArray m. For a 2-dimensional array, this is the number of rows. vaSize() returns -1 if the dimension to be queried does not exist.

Deleting Arrays

void vaDelete( VarArray );

Deallocates the storage for the specified VarArray. The value passed as VarArray must have been returned by a previous call to one of the vaCreate_...() functions.

Printing Arrays (C++ only)

std::ostream& vaPrint( std::ostream& os, VarArray, int indent=0 );

After including <var_array++.h> (instead of <var_array.h>), you can use the vaPrint() function to print the contents of an VarArray to the given ostream. indent specifies the indentation (number of spaces) of the output.

Example:

#include <iostream>
#include <var_array++.h>

int main(void)
{
  char ctab_[2][2] = {
    { 'a', 'b' },
    { 'c', 'd' }
  };
  // the following code line makes the build-in array ctab_
  // accessable as VarArray ctab:
  vaArray_2d(char) ctab = vaCreate_2d(2, 2, char, ctab_);

  std::cout << "Matrix:" << std::endl;
  vaPrint(std::cout, ctab);
  std::cout << "First line of matrix:" << std::endl;
  vaPrint(std::cout, ctab[0], 3);

  vaDelete(ctab);
}
The program above produces the following output:
Matrix:
[ [0]: [ [0]: a
         [1]: b
       ]
  [1]: [ [0]: c
         [1]: d
       ]
]
First line of matrix:
   [ [0]: a
     [1]: b
   ]

Download

This software is free but copyright © 2003-2006 by Forschungszentrum Jülich, ZAM, Germany. By downloading and using this software you automatically agree to comply with the regulations as described in the license agreement.

For further information (in German) see:



Forschungszentrum Jülich
D-52425 Jülich
ZAM
Div. Capability Computing and Visualisation
Information Imprint
25.01.2006
Günter Egerer