[comp.lang.c] Two dimensional arrays in C

murphys@cod.UUCP (Steven P. Murphy) (06/23/87)

The following example is what I would like to be able to do. Is it possible ?

I tried something like this twice but it didn't work either time, though 
it be from my inexperience with C.
  


#define ROWS1     500
#define COLUMNS1  30

#define ROWS2     400
#define COLUMNS2  20

main()
{
static double data1[ROWS][COLUMNS];
static double data2[ROWS][COLUMNS];

int n, m;

n1 = 300;
m1 = 15;
n2 = 200;
m2 = 18;
  .
  .
  .
matrix_stuff(n1, m1, data1, ROWS1, COLUMNS1);
  .
  .
  .
matrix_stuff(n2, m2, data2, ROWS2, COLUMNS2);
  .
  .
  .
}              /*   end  of  main   */



matrix_stuff(a, b, array, x, y);

int a, b, x, y;
double array[x][y];

{
int i, j;
   .
   .
   .
for(i = 0;i <= a;i++)                  /* step through the rows */
   for(j = 0;j <= b;j++)               /* step through the columns */
       {
         operate on the part of the
         matrix "array" that was used in main
       }
   .
   .
   .
}              /*   end  of  matrix_stuff   */

edw@ius2.cs.cmu.edu (Eddie Wyatt) (06/23/87)

In article <733@cod.UUCP>, murphys@cod.UUCP (Steven P. Murphy) writes:
> 
> 
> The following example is what I would like to be able to do. Is it possible ?
> 
> I tried something like this twice but it didn't work either time, though 
> it be from my inexperience with C.
>   
> 
> 
> #define ROWS1     500
> #define COLUMNS1  30
> 
> #define ROWS2     400
> #define COLUMNS2  20
> 
> main()
> {
> static double data1[ROWS][COLUMNS];
> static double data2[ROWS][COLUMNS];
> 
> int n, m;
> 
> n1 = 300;
> m1 = 15;
> n2 = 200;
> m2 = 18;
>   .
>   .
>   .
> matrix_stuff(n1, m1, data1, ROWS1, COLUMNS1);
>   .
>   .
>   .
> matrix_stuff(n2, m2, data2, ROWS2, COLUMNS2);
>   .
>   .
>   .
> }              /*   end  of  main   */
> 
> 
> 
> matrix_stuff(a, b, array, x, y);
> 
> int a, b, x, y;
> double array[x][y];
> 
> {
> int i, j;
>    .
>    .
>    .
> for(i = 0;i <= a;i++)                  /* step through the rows */
>    for(j = 0;j <= b;j++)               /* step through the columns */
>        {
>          operate on the part of the
>          matrix "array" that was used in main
>        }
>    .
>    .
>    .
> }              /*   end  of  matrix_stuff   */



   In the direct way, unfortunately no.  I can see no reason why C
doesn't support a construct like that, Fortrash does.  And
it wouldn't make the code generated much less efficient - the
index function could have the dimensions plugged in when the
matrix function is called.  Note, I said not much less efficient -
there may be  some optimizations that the compiler may realize
about the array indexes that it can't with "dynamic" bounds.

  One posible solution is to treat the matrix as an array of doubles
and write you own indexing function.

  Example :

	matrix_stuff(a,b,array,x,y)
	    int a,b,x,y;
	    double array[];
	    {
	    register int i,j;
		.....

	    for (i = 0; i <= a; i++)
	        for (j = 0; j <= b; j++)
		    {
		     submatrix[i][j] = array[i*y+j];
			/* indexing function  ^ */
			......
		    }
	    }

 In general, the indexing function in C for

	type a[N1][N2]...[Nn];  a[I1][I2]...[In] is

	*( sizeof(type)*(In + Nn*(I{n-1} +N{n-1}*(I{n-2} + N{n-2}....
		N3*(I2 + N2*I1))))...)

  By declaring the array "array" to be an array of  doubles you in a sence fill
in the sizeof value and  supply the pointer operation.

  This indexing function is use because arrays are in is that column
or row major form? - I keep forgetting which is which.

-- 
					Eddie Wyatt

e-mail: edw@ius2.cs.cmu.edu

terrorist, cryptography, DES, drugs, cipher, secret, decode, NSA, CIA, NRO.

gwyn@brl-smoke.ARPA (Doug Gwyn ) (06/23/87)

In article <733@cod.UUCP> murphys@cod.UUCP (Steven P. Murphy) writes:
>matrix_stuff(a, b, array, x, y);
>int a, b, x, y;
>double array[x][y];
>{

Sorry, that is not legal in C, although the analogous operation is
commonly encountered in Fortran programming.  In C, formal array
parameters must be declared with constant dimensions*, although the
leftmost dimension is unimportant (so that [1] or [] will do for it).
Keep in mind that an attempt to pass the name of an array as an actual
parameter in a function call results in a pointer to the first element
of the array as the real parameter.  Also remember that all arrays are
really 1-dimensional aggregates of objects, which in turn may be arrays.

This subject should be covered in any good C programming text, although
probably someone like Torek will post examples.  The point I wish to
make is that you can get into trouble if you try to take an "intuitive"
approach to using arrays in C; you need to carefully examine just what
the rules are.  Once you do that, you'll find several alternate ways to
accomplish what you're trying to do.

*I once suggested to Dennis Ritchie that this restriction on formal
array parameters could be removed, and although he seemed to agree that
there was no theoretical problem doing so, we agreed that it would add
complexity to compilers.  He seemed to think that the feature would be
of insufficient general utility to offset the added complexity.  (I
hope I haven't misrepresented his position; it was a long time ago.)

nate@altos86.UUCP (Nathaniel Ingersoll) (06/24/87)

In article <733@cod.UUCP> murphys@cod.UUCP (Steven P. Murphy) writes:
...declarations...
>static double data1[ROWS][COLUMNS];
>static double data2[ROWS][COLUMNS];

make that
	static double data1[ROWS1][COLUMNS1];
	static double data2[ROWS2][COLUMNS2];
>
>int n, m;
>
>n1 = 300;
>m1 = 15;
>  .
>matrix_stuff(n1, m1, data1, ROWS1, COLUMNS1);
>  .
>matrix_stuff(n2, m2, data2, ROWS2, COLUMNS2);
>}
>
>matrix_stuff(a, b, array, x, y);
>int a, b, x, y;
>double array[x][y];
>{
>int i, j;
>   .
>for(i = 0;i <= a;i++)                  /* step through the rows */
>   for(j = 0;j <= b;j++)               /* step through the columns */

The problem here is with the
	double array[x][y];
declaration.  You need to declare all the sizes of an arrays
dimensions except the first; and this declaration must be _constant_.
To do what you want you would probably have to do something like

/*
*	note that the x array bound is unneeded
*
*	various purists will probably be offended by the
*	following declaration for "array"
*/
matrix_stuff(a, b, array, x, y)
	int a, b, x, y;
	double *array;
{
#define		place(u, v)	((u)*y + (v))
	int i, j;

	/* following with your example, though you'd probably want
	to eliminate the a and b parameters  and use x and y instead */
	for (i = 0;i < a;i++)
		for (j = 0;j < b;j++)
/*
			...use array[place(i, j)] in here....
for example, this would  mutiply all elements of array by 2:
			array[place(i, j)] *= 2;
*/
}

franka@mmintl.UUCP (Frank Adams) (06/26/87)

In article <733@cod.UUCP> murphys@cod.UUCP (Steven P. Murphy) writes:
>The following example is what I would like to be able to do. Is it possible ?
>...
>matrix_stuff(a, b, array, x, y);
>
>int a, b, x, y;
>double array[x][y];

No.  Array bounds in C have to be constants.  To do this, you would have to
do the array arithmetic explicitly.  Sorry.
-- 

Frank Adams                           ihnp4!philabs!pwa-b!mmintl!franka
Ashton-Tate          52 Oakland Ave North         E. Hartford, CT 06108

chris@mimsy.UUCP (Chris Torek) (06/27/87)

In article <733@cod.UUCP> murphys@cod.UUCP (Steven P. Murphy) writes:
>matrix_stuff(a, b, array, x, y);
>int a, b, x, y;
>double array[x][y];
>{

[does not work.]

C does not allow variables as dimension, not even in parameter
declarations.  Given that the caller wants to pass two differently-
shaped matrices (one [500][30], one [400][20]), the only way to
do this without creating a vector of vectors is to have the caller
pass the address of mat[0][0]:

	#define M1A ... /* and likewise for 1B, 2A, 2B */
	main(...)
	{
		static double m1[M1A][M1B];
		static double m2[M2A][M2B];
		...
		matrix_stuff(a, b, &m1[0][0], M1B);/* M1A is irrelevant */
		matrix_stuff(c, d, &m2[0][0], M2B);/* likewise */
		...
	}

	matrix_stuff(a, b, array, y)
		int a, b, y;
		double *array;
	{
		int i, j;

		for (i = 0; i < a; i++) {
			for (j = 0; j < b; j++) {
				v = array[i * y + j];
				...
				array[i * y + j] = v;
			}
		}
	}

If the `shape' of the two matrices is the same---that is, all
dimensions save the first are the same---you can do this:

	main(...) {
		static double m1[R1][C], m2[R2][C];

		...
		matrix_stuff(a, b, m1);
		matrix_stuff(c, d, m2);
		...
	}

	matrix_stuff(a, b, array)
		int a, b;
		double (*array)[C];
	{
		int i, j;

		for (i = 0; i < a; i++)
			for (j = 0; j < b; j++)
				/* work on array[i][j] here */;
	}

This generates approximately the same code, except that instead of
having `array[i * <variable> + j]', the compiler can generate
`array[i * C + j]'.  In either case a good optimiser will pull the
`i * expr' expression out of the `for j' loop, but the chance that
your compiler has a good optimiser is rather slim.

The method I prefer is vectors of vectors:

	struct matrix {
		double	**m_data;	/* vectors of (double *) */
		int	m_rows;		/* number of rows */
		int	m_cols;		/* and columns */
	};

	struct matrix *make_matrix();

	main()
	{
		struct matrix *m1, *m2;
		...
		m1 = make_matrix(rows, cols);
		m1->m_data[i][j] = value;
		m2 = make_matrix(rows2, cols2);
		...
		matrix_stuff(m1);
		matrix_stuff(m2);
		...
	}

	matrix_stuff(m)
		register struct matrix *m;
	{
		register int i, j;

		for (i = 0; i < m->m_rows; i++)
			for (j = 0; j < m->m_cols; j++)
				/* do things with m->m_data[i][j] */;
	}

	struct matrix *
	make_matrix(nr, nc)
		register int nr, nc;
	{
		register struct matrix *m;
		register double **p;
	#define alloc(v, n, t) \
		if ((v = (t *) malloc(n * sizeof (t))) != NULL); \
		else abort_because_out_of_memory()
						/* Allocate: */
		alloc(m, 1, struct matrix);	/* the matrix; */
		alloc(p, nr, double *);		/* the vector of vectors; */
		m->m_rows = nr;
		m->m_cols = nc;
		m->m_data = p;
		while (--nr >= 0)		/* and each vector */
			alloc(*p++, nc, double);/* of doubles. */
	#undef alloc
		return (m);
	}

This is `neater' in that the size of the matrix is carried about
with the matrix itself, and no multiplication is necessary to find
any matrix element.  The definition of alloc() is not suitable for
a general matrix library, since it aborts the program if it cannot
get enough memory, but this should suffice for illustration.

Note that you can dynamically allocate `flat' matrices, too:

	struct flatmat {
		double	*fm_data;
		int	fm_rows, fm_cols;
	};
	#define FM_ELT(fm, i, j) ((fm)->fm_data[(i) * (fm)->fm_cols + (j)])
	...
	struct flatmat *
	flatmat_alloc(nr, nc) {
		...
		alloc(fm, 1, struct flatmat);
		alloc(fm->fm_data, nr * nc, double);
		fm->fm_rows = nr;
		fm->fm_cols = nc;
		return (fm);
	}

but this requires all those multiplies (again, unless you have a
good optimiser), and precludes such things as upper triangular
matrices.  More complex methods of remembering the size and shape
of each matrix are appropriate in some situations.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	seismo!mimsy!chris

beede@hubcap.UUCP (Mike Beede) (06/30/87)

in article <2215@mmintl.UUCP>, franka@mmintl.UUCP (Frank Adams) says:
] 
] In article <733@cod.UUCP> murphys@cod.UUCP (Steven P. Murphy) writes:
]>The following example is what I would like to be able to do. Is it possible ?
]>...
]>matrix_stuff(a, b, array, x, y);
]>
]>int a, b, x, y;
]>double array[x][y];
] 
] No.  Array bounds in C have to be constants.  To do this, you would have to
] do the array arithmetic explicitly.  Sorry.

However, you CAN do the arithmetic explicitly if you like, using macros
to maintain fair readability:  something like

#define matrix_el(array,i,j) (array[y*sizeof(double)+j])

which produces an l-valued expression.  At the same time, you can insert
temporary bounds-checking until you're sure your code is correct (or
forever, whichever is smaller :-) ).  Now, of course, your declaration
for array is
 double array[];
and you can have things like matrix_el(A,i,j) = matrix_el(A,i+1,j) , etc.

Of course, since this depends on the value of y, it is still
sloppy, but you can include y as a discriptor bolted onto the array or
something similar, if you like (which the macro would reference).
-- 
Mike Beede                      UUCP: . . . !gatech!hubcap!beede
Computer Science Dept.          ARPA: BEEDE@TECNET-CLEMSON.ARPA
Clemson University              INET: beede@hubcap.clemson.edu
Clemson SC 29631-1906           PH: (803)656-{2845,3444}

beede@hubcap.UUCP (Mike Beede) (07/01/87)

in article <238@hubcap.UUCP>, beede@hubcap.UUCP (Me) says:
> 
> #define matrix_el(array,i,j) (array[y*sizeof(double)+j])
> 
which of course is completely bogus.  I'm not sure _where_ that
sizeof(double) popped in, but anyway the correct version is

#define matrix_el(array,i,j) (array[i*y+j]) ,

as a kind reader pointed out by email--thanks.  I guess I'll see a few cards
and letters on this one.

P.S.,

 Well, not _completely_ bogus -- it _does_ produce an l-valued expression :-)  
-- 
Mike Beede                      UUCP: . . . !gatech!hubcap!beede
Computer Science Dept.          ARPA: BEEDE@TECNET-CLEMSON.ARPA
Clemson University              INET: beede@hubcap.clemson.edu
Clemson SC 29631-1906           PH: (803)656-{2845,3444}

kurt@hi.UUCP (Kurt Zeilenga) (07/02/87)

He writes:
>in article <238@hubcap.UUCP>, beede@hubcap.UUCP (Me) says:
>> 
>> #define matrix_el(array,i,j) (array[y*sizeof(double)+j])
>> 
>which of course is completely bogus.  I'm not sure _where_ that
>sizeof(double) popped in, but anyway the correct version is
>
>#define matrix_el(array,i,j) (array[i*y+j]) ,
>

Actually, (I'm being picky) a better version is:

#define matrix_el(array,i,j)	array[(i)*y+(j)]

You should always use '(' and ')' to surround arguments in macros (and the
macro itself if needed) to insure the evaulation order of any expression
in the expanding macro.  Mike's macro, if used like matrix(array,i+1,j),
would return something a bit different then advertised.

-- 
	Kurt Zeilenga	 (zeilenga@hc.dspo.gov)		I want my talk.flame!

	"So long, Mom, I'm off to kill a commie..."

beede@hubcap.UUCP (Mike Beede) (07/03/87)

in article <245@hubcap.UUCP], beede@hubcap.UUCP (ME AGAIN) says:
] in article <238@hubcap.UUCP>, beede@hubcap.UUCP (Me) says:
]> #define matrix_el(array,i,j) (array[y*sizeof(double)+j])
]> 
] which of course is completely bogus.  I'm not sure _where_ that
] sizeof(double) popped in, but anyway the correct version is
] 
] #define matrix_el(array,i,j) (array[i*y+j]) ,
] 
Well--now the macro is correct if you don't use anything but single constants
or variables as the indexing arguments.  If we do something like

 matrix_el(A,k+5,m)

it expands to 

 (A[k+5*y+m])

which is surely bogus, since this is k+(5*y)+m instead of (k+5)*y+m.
The moral of the story, as Mark Brader was kind enough to point out
(twice!), is to always parenthesize the arguments of a macro when they
occur in the rhs, like the (REALLY) correct version:

#define matrix_el(array,i,j) (array[(i)*y+(j)]) ,

The sad part is, not only did Mark include the correct version in his
mail to me (which I discarded), but I myself was once burned by the
lack of parenthesis in a macro.

Sorry this took so long to iron out.
-- 
Mike Beede                      UUCP: . . . !gatech!hubcap!beede
Computer Science Dept.          ARPA: BEEDE@TECNET-CLEMSON.ARPA
Clemson University              INET: beede@hubcap.clemson.edu
Clemson SC 29631-1906           PH: (803)656-{2845,3444}

walters@osubem.UUCP (walters) (07/05/87)

What follows is some sample code that demonstrates using an 'array of
pointer to an array of double'.  This is a nice portable way to get
variable dimension arrays (I learned FORTRAsh before I learned C).  It's
great for swapping rows when doing matrix algebra and it also aviods
doing array indexing explicitly.  Obviously it could be used with other
types (ie.  integer).  Hope someone finds this useful. 

#include <stdio.h>
#include <math.h>

typedef double *RWP;
typedef struct
{
	RWP rw;
} A;

void do_something(a, r, c)
A a[];
int r, c;
{
	int i, j;
	RWP ai;

	for (i = 0; i < r; i++)
	{
		ai = a[i].rw;
		for (j = 0; j < c; j++)
			ai[j] = -ai[j];
	}
	return;
}

void do_something_else_another_way(a, r, c)
A a[];
int r, c;
{
	int i, j;

	for (i = 0; i < r; i++)
		for (j = 0; j < c; j++)
			a[i].rw[j] = fabs(a[i].rw[j]);
	return;
}

int arr_alloc(a, r, c)	/* allocate the array */
A **a;
int r, c;
{
	register int i, j;
	
	if ((*a = (A *) malloc(r * sizeof(A))) == (A *) NULL)
		return(0);
	for (i = 0; i < r; i++)
	{
		(*a)[i].rw = (RWP) malloc(c * sizeof(double));
		if ((*a)[i].rw == (RWP) NULL)
			return(0);
	}
	for (i = 0; i < r; i++)
		for (j = 0; j < c; j++)
			(*a)[i].rw[j] = 0.0;
	return(1);
}
						
void arr_free(a, r)	/* free the array */
A **a;
int r;
{
	register int i;
	
	for (i = 0; i < r; i++)
		free((*a)[i].rw);
	free(*a);
	return;
}	

#define MSIZE 10

main()
{
	static A *a;

	if (!arr_alloc(&a, MSIZE, MSIZE)) 
		abort();
	do_something(a, MSIZE, MSIZE);
	do_something_else_another_way(a, MSIZE, MSIZE);
	arr_free(&a, MSIZE);
}		

-----
Harold G. Walters                 Internet: walters@ce.okstate.edu
School of Civil Engineering       Uucp: {cbosgd, ihnp4, rutgers, seismo,
Oklahoma State University               uiucdcs}!okstate!osubem!walters
Stillwater, OK  74078

beede@hubcap.UUCP (Mike Beede) (07/05/87)

in article <254@hubcap.UUCP>, beede@hubcap.UUCP (Mike Beede) says:
> 
> #define matrix_el(array,i,j) (array[(i)*y+(j)]) ,

Well, Mark Brader strikes again, pointing out that the value of
``array'' could well be an expression, too.  I have to agree, so
the final version of this blasted thing is

#define matrix_el(array,i,j) ((array)[(i)*y+(j)])

(array being an expression is not too unlikely, given C programmers'
penchant for passing addresses like   buffer+offset  .  If you're doing
your own matrix sizing, you might be allocating said matrices out of
a big double array . . .  
-- 
Mike Beede                      UUCP: . . . !gatech!hubcap!beede
Computer Science Dept.          ARPA: BEEDE@TECNET-CLEMSON.ARPA
Clemson University              INET: beede@hubcap.clemson.edu
Clemson SC 29631-1906           PH: (803)656-{2845,3444}