[net.lang.c] dynamic array parameters

tribble_acn%uta.csnet@csnet-relay.arpa (David Tribble) (12/23/85)

Recently a question arose on the net about the problem of passing a multi-
dimensional array, with varying (or `dynamic') dimension bounds, to a
function.  (This is especially useful when dealing with dynamically
allocated arrays.)  This is one way to do it-

#define D1	3		/* array bounds		*/
#define D2	5
#define D3	9

int	a[D1][D2];		/* 2-D array of int	*/

... fnc(a, D1, D2) ...		/* call fnc, passes address of a */

fnc(a, d1, d2)
    int		*a;		/* pointer to int	*/
    int		d1, d2;		/* dimension bounds	*/
{
    int		i, j;

    ... (*(a + i*d1 + j)) ...	/* same as a[i][j]	*/
}

Or, to make things a little easier-

#define index(a,i,j)	(*(a + i*d1 + j))

    ... index(a, i, j) ...	/* same as a[i][j]	*/

Thus for more dimensions-

int	a[D3][D2][D1];		/* 3-D array of int	*/

... fnc(a, D1, D2, D3) ...	/* passes address of a	*/

#define index3(a,i,j,k)	(*(a + (i*d2 + j)*d1 + k))
fnc(a, d1, d2, d3)
    int		*a;		/* array int pointer	*/
    int		d1, d2, d3;	/* dimension bounds	*/
{
    int		i, j, k;

    ... index3(a, i, j, k) ...	/* same as a[i][j][k]	*/
}

Other indexing macros would be-
#define index1(a,i)		(*(a + i))
#define index2(a,i,j)		(*(a + i*d1 + j))
#define index3(a,i,j,k)		(*(a + (i*d2 + j)*d1 + k))
#define index4(a,i,j,k,l)	(*(a + ((i*d3 + j)*d2 + k)*d1 + l))
#define index5(a,i,j,k,l,m)	(*(a + (((i*d4 + j)*d3 + k)*d2 + l)*d1 + m))
Etc.

What you are doing is passing the address of the first element
of the array (int * a) and calculating the address of the array
element you want.  This is exactly what the compiler would
do if it knew the dimensions of the array.  (Because it is just
a pointer, the compiler does not know any dimensions to associate with
it.)

This has the interesting side effect that you can pass (for example)
a 5-dimensional array and treat it as (for example) a 3-dimensional
array.  This is because the parameter is declared as a 1-dimensional
array, and you are manually calculating the offset.

See Harbison & Steele section 5.5, "Array Types", especially section 5.5.2,
"Multidimensional Arrays".  This is also discussed in the white book.

/* run this program to see how it works */

#define D1              3
#define D2              4
#define D3              5

#define index3(a,i,j,k) (*(a + (i*D2 + j)*D1 + k))

main()
{
    int         a[D3][D2][D1];
    int         i, j, k, l;

    for (i = 0; i < D3; ++i) {
        for (j = 0; j < D2; ++j) {
            for (k = 0; k < D1; ++k) {
                a[i][j][k] = i*100 + j*10 + k;
            }
        }
    }
    fnc(a, D3, D2, D1);
}

fnc(a, dim3, dim2, dim1)
int	* a;
int	dim3, dim2, dim1;
{
    int		i, j, k;

    for (i = 0; i < dim3; ++i) {
        for (j = 0; j < dim2; ++j) {
            for (k = 0; k < dim1; ++k) {
                printf("a[%d][%d][%d] = %03d\n",
                    i, j, k, index3(a, i, j, k));
            }
        }
    }
}

Hope this helps.

throopw@dg_rtp.UUCP (Wayne Throop) (12/26/85)

To reference an element of a variably sized multi-dimensional array,
David Tribble recommends

>  (*(a + i*d1 + j))    /* same as a[i][j]	*/

along with using macros to simplify, like so:

> #define index(a,i,j)  (*(a + i*d1 + j))
>  index(a, i, j)       /* same as a[i][j]	*/

Good idea.  I'd just like to note that, using pointer/array equivalence,
the somewhat complicated expression can be written a little less
confusingly, as

        a[i*d1 + j]

It might still be advisable to use the macro, but then again, maybe not.
The more-than-two-dimensional cases are still pretty confusing:

        a[(i*d1 + j)*d2 + k]

Also, I'd recommend declaring the formal "a" as "int a[]" rather than
"int *a", unless it is important for some reason to treat "a" as an
lvalue.
-- 
Wayne Throop at Data General, RTP, NC
<the-known-world>!mcnc!rti-sel!dg_rtp!throopw

throopw@dg_rtp.UUCP (Wayne Throop) (12/26/85)

A small PS.  Naturally, the macro

> #define index(a,i,j)  (*(a + i*d1 + j))

really needs extra parenthesization, like so:

#define index(a,i,j) (*((a) + (i)*d1 + (j)))

or, (using subscripts) like so:

#define index(a,i,j) ((a)[(i)*d1 + (j)])
-- 
Wayne Throop at Data General, RTP, NC
<the-known-world>!mcnc!rti-sel!dg_rtp!throopw