From: dfitts@carson.u.washington.edu (Douglas Fitts)
Newsgroups: comp.lang.c
Subject: Re: Accessing a dynamical 3-D field
Summary: Functions for dynamic allocation with variable bounds
Date: 31 Dec 92 20:43:00 GMT
Article-I.D.: shelley.1hvm0kINN6l2
Distribution: world
Organization: University of Washington, Seattle
NNTP-Posting-Host: carson.u.washington.edu

In article <1992Dec29.142134.25641@Informatik.TU-Muenchen.DE> backscha@Informatik.TU-Muenchen.DE (Martin Backschat) writes:
>
>In my program I use 3-D fields of doubles with variable bounds.
>This field is implemented as an array of doubles which is allocated
>dynamically with malloc.
>To access for example the [i][j][k] component in the field f of dimension U,V,W
>I have to write   f[i*V*W + j*W + k].
>
>Now my question:
>
>Is it somehow possible to declare and allocate a 3-D field u (of doubles) with
>variable bounds, allocated by malloc, in such a way that it can be accessed
>with
>u[i,j,k] (with type casting)?


Yes, if u[i][j][k] will do instead of u[i,j,k].

Here are functions similar to ones I use to allocate and free 3-D 
variably-dimensioned arrays in MS C7.  I've modified this, so there 
may be bugs, but it runs with the main() here.  To be less portable 
and more flexible, use the "_far" keyword and _fmalloc().  As the 
comments say, first declare an appropriate pointer to serve as the base 
of the array, then set it equal to the return value using a cast to the 
type.  In your case this would be double, but it could be char, unsigned, 
or long double - whatever.  

Access the array as buffer[1][2][0], etc.  As with any array in C, 
be very careful not to overrun the length of any of these arrays - 
always access using the same bounds values & arrays you pass to 
this function.   Also, always immediately check for a NULL buffer 
on return in case you've run out of memory in the segment 
(or write these as _huge if your compiler supports it).   

Always free using the special function free3D().  You have to pass 
the freeing function the address of buffer, not just buffer, because you 
can't set the base pointer to NULL otherwise.  Note that the parens
around (*a) are important.  The "if" statements are necessary in case
the call to free3D() is coming from dim3D() with an incompletely
populated array.  It's not necessary to pass array nfst, because the
memory manager remembers how many of these there were (in the heap).  

E-mail me if you have any trouble.  Hope it's what you want.






#include <malloc.h> /* _fcalloc(), calloc() */
#include <stdio.h>	/* printf(), NULL */
#include <stdlib.h> /* exit (), for main() only; ANSI calloc() */

/* function prototypes */
int main( void );
void  ***dim3D( int, int *, int *, size_t );
void free3D( void ****, int, int * );

void ***dim3D( int slow, int *med, int *fast, size_t bytes )
{
    /* slow, dimension of slowest moving pointer */
    /* med, array of intermediate pointers */
    /* fast, array of fastest moving pointers */
    /* bytes, size of data type for final array */
    /* Function call example: */
    /* double  ***buffer; */
    /* int nslo, *nmed, *nfst; */ 
    /* ...   allocate and fill arrays nmed and nfst */                                  
    /* buffer = (double ***) dim3D( nslo, nmed, nfst, sizeof(double) ); */
    /* if (buffer == NULL) { do_disaster_recovery(); } */
    /* Free using free3D(). */
    /* Always check for NULL return buffer - it's easy to chew up 64K! */

	void *** a;
	register int i, j; 
   
	/* allocate space for an array of 'slow' pointers: */
	a = (void ***)calloc( slow, sizeof(void **) );
	if (a == NULL) { return(NULL); }   /* check after every allocation */
	else
		{
		for (i = 0; i < slow; i++)  /* for each 'slow' pointer */
			{
			/* allocate space for an array of med void * values */
			a[i] = (void **)calloc( med[i], sizeof(void *) );
			if (a[i] == NULL)
				{ 
				free3D( &a, slow, med ); 
				return(NULL); 
				}	
			for (j = 0; j < med[i]; j++) /* for each med pointer */
				{
				a[i][j] = (void *)calloc( fast[j], bytes );
				if (a[i][j] == NULL)
					{ 
					free3D( &a, slow, med ); 
					return(NULL); 
					}
				}
			}
		return(a);  /* return *** to void */
		}
}

void free3D(void **** a, int slow, int *med)
{
	/* Frees memory allocated by dim3D() */

	/* usage:
		double *** buffer;
		int nslo, *nmed, *nfst;  
		...    allocate and fill arrays nmed and nfst 
		buffer = (double ***)dim3D( nslo, nmed, nfst, sizeof(double) );
		free3D( &buffer, nslo, nmed ); 
    	        free( nmed );
    	        free( nfst );
	*/

	register int j, i;

	for(i = slow-1; i >= 0; i--)   /* starting with last row */
		{
		for (j = med[i]-1; j >= 0; j--)
			{
			if ((*a)[i][j]) free ((*a)[i][j]);  /* free array */
			(*a)[i][j] = NULL; /* & set to NULL until all freed. */
			}
		if ((*a)[i]) free ((*a)[i]);
		(*a)[i] = NULL;
		}
	if (*a) free (*a);                /* free the pointer to pointer */
	*a = NULL;
}

int main( void )
{
    /* sample driver program */
    double *** buffer;
    int nslo, *nmed, *nfst;
    register int i, j, k;
    
    /* You fill in these arbitrary assignments at run-time: */
    nslo = 2;   /* arbitrary assignment number of levels */
    nmed = (int *)calloc( nslo, sizeof(int) );
    nmed[0] = 3; /* arbitrary assignment number of levels */
    nmed[1] = 2; /* arbitrary assignment number of levels */
    nfst = (int *)calloc( (3+2), sizeof(int) );
    nfst[0] = 4; /* arbitrary assignment number of levels */
    nfst[1] = 2; /* arbitrary assignment number of levels */
    nfst[2] = 3; /* arbitrary assignment number of levels */
    nfst[3] = 4; /* arbitrary assignment number of levels */
    nfst[4] = 3; /* arbitrary assignment number of levels */
    
    /* So, there are arbitrarily 2 levels of variable nslo,
    	3 and 2 levels of nmed at the two levels of nslo, and
        4, 2, and 3 levels of nfst at the 3 levels of nmed[0], and
        4 and 3 levels of nfst at the 2 levels of nmed[1].
    */
        
    buffer = (double ***)dim3D( nslo, nmed, nfst, sizeof(double) );
    if (buffer == NULL) exit (-1);
    for (i = 0; i < nslo; i++)
    	for (j = 0; j < nmed[i]; j++)   /* notice nmed[i], not nmed[j] */
        	for (k = 0; k < nfst[j]; k++)  /* nfst[j], not nfst[k] */
            	buffer[i][j][k] = (double)rand();  /* random double */ 
    for (i = 0; i < nslo; i++)
        {
    	printf("\n\nFactor nslo level %i\n", i);
    	for (j = 0; j < nmed[i]; j++)   /* notice nmed[i], not nmed[j] */
            {
    		printf("\nFactor nmed level %i\n", j);
        	for (k = 0; k < nfst[j]; k++)  /* nfst[j], not nfst[k] */
    			printf("%lf ", buffer[i][j][k]);    
            }
        }
    free3D( (void ****)&buffer, nslo, nmed ); 
    free( nmed );
    free( nfst );
    exit (0);
}



Doug Fitts
University of Washington, Seattle
Internet: dfitts@u.washington.edu
Newsgroups: comp.lang.c