[comp.lang.c] 2D arrays and pointers in ANSI C.

christos@batcomputer.tn.cornell.edu (Christos S. Zoulas) (12/02/90)

In article <11616.2757eaf1@ecs.umass.edu> lim@ecs.umass.edu writes:
>Hi,
>
>I'd appreciate any answers you may have to the following problem. 
> It's about 2D arrays and pointers in ANSI C.
> ...
>I'm aware that it's possible to use just pointers (*) to access the array, but 
>have had no luck trying to use something like matrix[i][j] in the test_matrix 
>function. Is it possible to use matrix[i][j] at all? I've had no problem using 
>matrix[i] for a *matrix argument (1D array).

I think the following piece of code will solve your problem:

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README Makefile arrayn.c bigtest.c smalltest.c
# Wrapped by christos@guillemin on Sun Dec  2 07:52:19 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(467 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X#
X# README Fri Sep  8 02:12:57 EDT 1989
X#
XThis a a dynamic memory allocator for multi-dimensional arrays.
XIt computes the size of the array, (size of data + size of pointers).
XThen it allocates the array using malloc, and threads the pointers
Xto form a multi-dimensional array.
XThe source is in arrayn.c, and with it there are 2 test programs
Xsmalltest.c and bigtest.c
X
X
XPlease send comments, bugfixes etc. to
X
Xchristos@tesla.ee.cornell.edu or
Xchristos@crnlee.bitnet
END_OF_FILE
if test 467 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(320 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X#	Makefile for arrayn
X#
XSHELL	= /bin/sh
X#CFLAGS	= -g -DDEBUG
XCFLAGS	= -O
X
Xall : smalltest bigtest
X	@echo "All done"
X
Xsmalltest : arrayn.o smalltest.o
X	cc ${CFLAGS} arrayn.o smalltest.o -o smalltest
X
Xbigtest : arrayn.o bigtest.o
X	cc ${CFLAGS} arrayn.o bigtest.o -o bigtest
X
Xarrayn.o : arrayn.c
X
Xclean   :
X	rm -f ${OBJ}
END_OF_FILE
if test 320 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'arrayn.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'arrayn.c'\"
else
echo shar: Extracting \"'arrayn.c'\" \(4107 characters\)
sed "s/^X//" >'arrayn.c' <<'END_OF_FILE'
X/* $Header: /home/hyperion/mu/christos/src/mine/lib/arrayn/RCS/arrayn.c,v 1.2 90/02/26 02:20:42 christos Exp $ */
X/*
X * arrayn.c: Dynamic memory allocator for an n - dimensional array.
X *
X * $Log:	arrayn.c,v $
X * Revision 1.2  90/02/26  02:20:42  christos
X * ANSI conformance.
X * 
X * Revision 1.1  89/09/08  02:23:03  christos
X * Initial revision
X * 
X */
X#ifndef lint
Xstatic char rcsid[] = "$Id: arrayn.c,v 1.2 90/02/26 02:20:42 christos Exp $";
X#endif /* lint */
X
X#ifdef DEBUG
X#include <stdio.h>
X#endif /* DEBUG */
X
X#ifndef __STDC__
X#include <varargs.h>
Xextern char *malloc();
Xtypedef char genptr_t;
X#else
X#include <stdarg.h>
Xextern void *malloc(unsigned n);
Xtypedef void genptr_t;
X#endif
X
Xtypedef unsigned long	u_long;
X
Xtypedef struct _dimension_t {
X    u_long    	size;		/* Number of elements in the dimension	*/
X    u_long 	totalsize;	/* Total number of elements 		*/
X    u_long	elsize;		/* Size of a single element in bytes	*/
X    u_long    	offset;		/* offset from beginning of array	*/
X} dimension_t;
X
X/* arrayn():
X *	Allocate an n-dimensional array. 
X *	We want to allocate only one chunk, so that free
X *	will free the whole array.
X */
X#ifndef __STDC__
Xchar *
X/*VARARGS*/
Xarrayn(numdim, va_alist)
Xint 	numdim;			/* Number of dimensions			*/
Xva_dcl
X#else
Xvoid *
Xarrayn(int numdim, ...)
X#endif
X{
X    int 	i, j;
X    dimension_t	*dim;		/* Info about each dimension		*/
X    u_long	total_size, 	/* Total size of the array		*/
X		pointer_size, 	/* Pointer size of the array		*/
X		element_size; 	/* Element size of the array		*/
X    genptr_t	*array;		/* the multi-dimensional array		*/
X    genptr_t	**ptr, *data;	/* Pointer and data offsets		*/
X    va_list 	va;		/* Current pointer in the argument list	*/
X
X#ifndef __STDC__
X    va_start(va);
X#else
X    va_start(va, numdim);
X#endif
X
X    if ( (dim = (dimension_t *) 
X	        malloc( (unsigned) (numdim * sizeof(dimension_t)))) 
X	 == (dimension_t *) 0 ) 
X	return((genptr_t *) 0);
X
X    for ( i = 0; i < numdim; i++ ) {
X	dim[i].size = va_arg(va, u_long);
X	dim[i].elsize = sizeof(genptr_t *);
X    }
X    dim[numdim-1].elsize = va_arg(va, u_long);
X
X    va_end(va);
X    
X    /*
X     *	Compute the size of the array to be allocated 
X     *	elements : (x * y * z ... w * elementsize)
X     *	pointers : (x * pointersize +
X     *              y * x * pointersize +
X     *              z * y * x * pointersize ..
X     */
X    dim[0].totalsize = dim[0].size;
X    dim[0].offset    = 0;
X    for ( i = 1; i < numdim; i++ ) {
X	dim[i].totalsize = dim[i-1].totalsize * dim[i].size;
X	dim[i].offset = dim[i-1].offset + dim[i-1].totalsize * dim[i-1].elsize;
X    }
X
X    element_size = dim[numdim-1].offset + 
X			dim[numdim-1].totalsize * dim[numdim-1].elsize;
X    pointer_size = dim[numdim-1].totalsize;
X
X    total_size = element_size + pointer_size;
X
X#ifdef DEBUG
X    (void) fprintf(stderr, "%20s : %10d\n", "Number of dimensions", 
X	numdim, numdim);
X    (void) fprintf(stderr, "%20s : %10d (%.8x)\n", "Unit size", 
X	dim[numdim-1].elsize, dim[numdim-1].elsize);
X    (void) fprintf(stderr, "%20s : %10d (%.8x)\n", "Size of pointers", 
X	pointer_size, pointer_size);
X    (void) fprintf(stderr, "%20s : %10d (%.8x)\n", "Size of elements", 
X	element_size, element_size);
X    (void) fprintf(stderr, "%20s : %10d (%.8x)\n", "Total size", 
X	total_size, total_size);
X    (void) fprintf(stderr, "\nDimension\tSize\tTotal Size\tOffset\tElement\n");
X    for ( i = 0; i < numdim; i++ ) 
X	(void) fprintf(stderr, "%9d\t%4d\t%10d\t%6d\t%7d\n", i, dim[i].size,
X		dim[i].totalsize, dim[i].offset, dim[i].elsize);
X#endif /* DEBUG */
X
X    /*
X     * Allocate space to hold the array
X     */
X    if ( (array = (genptr_t *) malloc((unsigned) total_size)) == 
X	 (genptr_t *) 0 ) {
X	(void) free((genptr_t *) dim);
X	return((genptr_t *) 0);
X    }
X
X    /*
X     * Thread the pointers
X     */
X    for ( i = 0; i < numdim - 1; i++ ) {
X	ptr  = (genptr_t **) (array + dim[i].offset);
X	data = (genptr_t *) (array + dim[i+1].offset);
X	for ( j = 0; j < dim[i].totalsize; j++ ) 
X	    ptr[j] = data + j * dim[i+1].elsize * dim[i+1].size;
X    }
X
X    (void) free((genptr_t *) dim);
X    return(array);
X
X} /* end arrayn */
END_OF_FILE
if test 4107 -ne `wc -c <'arrayn.c'`; then
    echo shar: \"'arrayn.c'\" unpacked with wrong size!
fi
# end of 'arrayn.c'
fi
if test -f 'bigtest.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bigtest.c'\"
else
echo shar: Extracting \"'bigtest.c'\" \(3020 characters\)
sed "s/^X//" >'bigtest.c' <<'END_OF_FILE'
X/* $Header: /home/hyperion/mu/christos/src/mine/lib/arrayn/RCS/bigtest.c,v 1.1 89/09/08 02:23:05 christos Exp Locker: christos $ */
X/*
X * bigtest.c: Test for arrayn.
X *
X * $Log:	bigtest.c,v $
X * Revision 1.1  89/09/08  02:23:05  christos
X * Initial revision
X * 
X */
X#ifndef lint
Xstatic char rcsid[] = "$Id: bigtest.c,v 1.1 89/09/08 02:23:05 christos Exp Locker: christos $";
X#endif /* lint */
X
X#include <stdio.h>
X
Xtypedef struct test_t {
X    int i, j, k, l, m;
X} test_t;
X	
Xextern char *arrayn();
X
X#define DIMNUM	 5
X#define D_I	 12
X#define	D_J	 23
X#define	D_K	 51
X#define	D_L	 4
X#define	D_M	 3
X
Xstatic char *pname;
X
X/*ARGSUSED*/
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    int i, j, k, l, m;
X    test_t *****ptr;
X
X    pname = *argv;
X
X    if ((ptr = (test_t *****) 
X	       arrayn(DIMNUM, D_I, D_J, D_K, D_L, D_M, sizeof(test_t))) ==
X	(test_t *****) 0 ) {
X	(void) fprintf(stderr, "%s: Out of memory.\n", pname);
X	exit(1);
X    }
X
X#ifdef DEBUG
X    for ( i = 0; i < D_I; i++ )
X	(void) fprintf(stderr, "ptr[%d] = %.8x(%.8x)\n", 
X	    i, &ptr[i], ptr[i]);
X
X    for ( i = 0; i < D_I; i++ )
X	for ( j = 0; j < D_J; j++ )
X	    (void) fprintf(stderr, "ptr[%d][%d] = %.8x(%.8x)\n", 
X		i, j, &ptr[i][j], ptr[i][j]);
X
X    for ( i = 0; i < D_I; i++ )
X	for ( j = 0; j < D_J; j++ )
X	    for ( k = 0; k < D_K; k++ ) 
X		(void) fprintf(stderr, "ptr[%d][%d][%d] = %.8x(%.8x)\n", 
X		    i, j, k, &ptr[i][j][k], ptr[i][j][k]);
X
X    for ( i = 0; i < D_I; i++ )
X	for ( j = 0; j < D_J; j++ )
X	    for ( k = 0; k < D_K; k++ ) 
X		for ( l = 0; l < D_L; l++ ) 
X		    (void) fprintf(stderr, "ptr[%d][%d][%d][%d] = %.8x(%.8x)\n",
X			i, j, k, l,  &ptr[i][j][k][l], ptr[i][j][k][l]);
X
X    for ( i = 0; i < D_I; i++ )
X	for ( j = 0; j < D_J; j++ )
X	    for ( k = 0; k < D_K; k++ ) 
X		for ( l = 0; l < D_L; l++ ) 
X		    for ( m = 0; m < D_M; m++ ) 
X			(void) fprintf(stderr, 
X			    "ptr[%d][%d][%d][%d][%d] = %.8x\n",
X			    i, j, k, l, m, ptr[i][j][k][l][m]);
X#endif /* DEBUG */
X    /*
X     *	Load each element with unique data
X     */
X    for ( i = 0; i < D_I; i++ )
X	for ( j = 0; j < D_J; j++ )
X	    for ( k = 0; k < D_K; k++ )
X		for ( l = 0; l < D_L; l++ )
X		    for ( m = 0; m < D_M; m++ ) {
X			ptr[i][j][k][l][m].i = i;
X			ptr[i][j][k][l][m].j = j;
X			ptr[i][j][k][l][m].k = k;
X			ptr[i][j][k][l][m].l = l;
X			ptr[i][j][k][l][m].m = m;
X		    }
X
X    for ( i = 0; i < D_I; i++ )
X	for ( j = 0; j < D_J; j++ )
X	    for ( k = 0; k < D_K; k++ )
X		for ( l = 0; l < D_L; l++ )
X		    for ( m = 0; m < D_M; m++ ) 
X			if ( ptr[i][j][k][l][m].i != i || 
X			     ptr[i][j][k][l][m].j != j ||
X			     ptr[i][j][k][l][m].k != k || 
X			     ptr[i][j][k][l][m].l != l || 
X			     ptr[i][j][k][l][m].m != m) {
X			    (void) fprintf(stderr, 
X	    "%s: element ptr[%d][%d][%d][%d][%d] bad (%d, %d, %d, %d, %d)\n",
X				pname, i, j, k, l, m, 
X				ptr[i][j][k][l][m].i, 
X				ptr[i][j][k][l][m].j,
X				ptr[i][j][k][l][m].k,
X				ptr[i][j][k][l][m].l,
X				ptr[i][j][k][l][m].m);
X			    exit(1);
X			}
X    (void) fprintf(stderr, "arrayn ok.\n");
X    exit(0);
X} /* end main */
END_OF_FILE
if test 3020 -ne `wc -c <'bigtest.c'`; then
    echo shar: \"'bigtest.c'\" unpacked with wrong size!
fi
# end of 'bigtest.c'
fi
if test -f 'smalltest.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'smalltest.c'\"
else
echo shar: Extracting \"'smalltest.c'\" \(2175 characters\)
sed "s/^X//" >'smalltest.c' <<'END_OF_FILE'
X/* $Header: /home/hyperion/mu/christos/src/mine/lib/arrayn/RCS/smalltest.c,v 1.1 89/09/08 02:23:07 christos Exp Locker: christos $ */
X/*
X * smalltest.c: Test for arrayn.
X *
X * $Log:	smalltest.c,v $
X * Revision 1.1  89/09/08  02:23:07  christos
X * Initial revision
X * 
X */
X#ifndef lint
Xstatic char rcsid[] = "$Id: smalltest.c,v 1.1 89/09/08 02:23:07 christos Exp Locker: christos $";
X#endif /* lint */
X
X#include <stdio.h>
X
Xtypedef struct test_t {
X    int i, j, k;
X} test_t;
X	
Xextern char *arrayn();
X
X#define DIMNUM	 3
X#define D_I	 4
X#define	D_J	 5
X#define	D_K	 6
X
Xstatic char *pname;
X
X/*ARGSUSED*/
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    int i, j, k;
X    test_t ***ptr;
X
X    pname = *argv;
X
X    if ((ptr = (test_t ***) arrayn(DIMNUM, D_I, D_J, D_K, sizeof(test_t))) ==
X	(test_t ***) 0 ) {
X	(void) fprintf(stderr, "%s: Out of memory.\n", pname);
X	exit(1);
X    }
X
X#ifdef DEBUG
X    for ( i = 0; i < D_I; i++ )
X	(void) fprintf(stderr, "ptr[%d] = %.8x(%.8x)\n", 
X	    i, &ptr[i], ptr[i]);
X
X    for ( i = 0; i < D_I; i++ )
X	for ( j = 0; j < D_J; j++ )
X	    (void) fprintf(stderr, "ptr[%d][%d] = %.8x(%.8x)\n", 
X		i, j, &ptr[i][j], ptr[i][j]);
X
X    for ( i = 0; i < D_I; i++ )
X	for ( j = 0; j < D_J; j++ )
X	    for ( k = 0; k < D_K; k++ ) 
X		(void) fprintf(stderr, "ptr[%d][%d][%d] = %.8x\n", 
X		    i, j, k, ptr[i][j][k]);
X#endif /* DEBUG */
X
X    /*
X     *	Load each element with unique data
X     */
X    for ( i = 0; i < D_I; i++ )
X	for ( j = 0; j < D_J; j++ )
X	    for ( k = 0; k < D_K; k++ ) {
X		ptr[i][j][k].i = i;
X		ptr[i][j][k].j = j;
X		ptr[i][j][k].k = k;
X	    }
X
X    for ( i = 0; i < D_I; i++ )
X	for ( j = 0; j < D_J; j++ )
X	    for ( k = 0; k < D_K; k++ ) {
X#ifdef DEBUG
X		(void) fprintf(stderr, 
X		    "ptr[%d][%d][%d] = (%d, %d, %d)\n",
X		    i, j, k, ptr[i][j][k].i, ptr[i][j][k].j,
X		    ptr[i][j][k].k);
X#endif /* DEBUG */
X
X		if ( ptr[i][j][k].i != i || ptr[i][j][k].j != j ||
X		     ptr[i][j][k].k != k ) {
X		    (void) fprintf(stderr, 
X			"%s: element ptr[%d][%d][%d] bad (%d, %d, %d)\n",
X			pname, i, j, k, ptr[i][j][k].i, ptr[i][j][k].j,
X			ptr[i][j][k].k);
X		    exit(1);
X		}
X	    }
X    (void) fprintf(stderr, "arrayn ok.\n");
X    exit(0);
X} /* end main */
END_OF_FILE
if test 2175 -ne `wc -c <'smalltest.c'`; then
    echo shar: \"'smalltest.c'\" unpacked with wrong size!
fi
# end of 'smalltest.c'
fi
echo shar: End of shell archive.
exit 0
-- 
   /------------------------------------------------------------------------\
   | Christos Zoulas         | 389 Theory Center, Electrical Engineering,   |
   | christos@ee.cornell.edu | Cornell University, Ithaca NY 14853.         |
   | christos@crnlee         | Phone: Disconnected  |   Fax: (607) 254 4565 |

lim@ecs.umass.edu (12/04/90)

Thanks to all who answered my question about 2D arrays and pointers in ANSI C.
Here's a summary of what I'm using:

int main(void)
{
 double **matrix    /* matrix is a 1D array of pointers; each pointer repre- */
 int m = ?, n = ?;  /* sents the address of a 1D array of doubles.           */
 int i;
 

 /* For a m x n matrix */
 matrix = (double **) malloc(m * sizeof(double *));
 
 /* I use calloc to initialize the elements to zero */
 for ( i = 0; i < m; i++ )
   matrix[i] = (double *) calloc(n,sizeof(double));

 test_matrix(matrix,m,n);
}

void test_matrix(double **matrix, int m, int n)
{
 int i, j;

 for ( i = 0; i < m; i++ )
   { 
    for ( j = 0; j < n; j++ )
      matrix[i][j] = 4.0;
   }

 .
 .
 .
}


Jonathan Lim

karl@ima.isc.com (Karl Heuer) (12/05/90)

In article <1990Dec2.125713.19371@batcomputer.tn.cornell.edu> christos@batcomputer.tn.cornell.edu (Christos S. Zoulas) writes:
>I think the following piece of code will solve your problem:
>[code to implement a function that purports to allow allocations like:]
>	ptr = (thing **)arrayn(2, M, N, sizeof(thing));
>	... ptr[i][j] ...

Although this probably works fine on vaxlike architectures, it will fail on
machines where `void *' and `thing *' have different internal representations
(such as DG).  This cannot be fixed without help from a compiler builtin, and
hence any program that uses arrayn() is unportable.

The only way this could become portable would be if the Standard were to
mandate its existence% (and hence require nonvaxlike implementations to supply
a simple crutch as a compiler builtin).  The Committee probably won't do so
unless there's existing practice, and they probably won't accept existing
practice because it's unportable code.

(Nice job, though.  I never bothered with more than 2D in my implementation,
before I threw it out for the reason described above.)

Karl W. Z. Heuer (karl@ima.isc.com or uunet!ima!karl), The Walking Lint
________
% The "standardizable" version would have to be a macro which expects one
  parameter to be a type name (e.g. array2(thing, M, N)), since "sizeof" might
  throw out too much information.  This is a further complication for the
  generic version, since C does not support variadic macros.