[comp.lang.c] How to pass arbitrary 2-d array argument ?

luj@delta.ecn.purdue.edu (Jun Lu) (01/06/91)

It would be nice if I can have a "subroutine" which takes arbitrary matrices as
arguments and performs some operations on them. The dimensions of the 2-d
array are not known a priori.  In other words, how do you add two
matrices together portably in C ? ( see following scenario descriptions )

main()
{
     double m1[4][2], m2[4][2], m[4][2];
     double a[5][5], b[5][5], c[5][5];

     /* init m1, m2 ... */


     madd(m1, m2, m, 4, 2);
     madd(a, b, c, 5, 5);

     /* ... */

}

madd(a, b, c, m, n)
 /* args --- how ???(Note n is not known at compile time, but we assume that
    the type of the 2-d array elements are known, i.e. double */
{
    /*
     * some work --- how ????
     */

     for (i = 0; i < m; i++)		/* fucntionality of madd() */
	for (j = 0; j < n; j++)
	    c[i][j] = a[i][j] + b[i][j];

}

No calling Fortran from C please. 

Any suggestions/comments/pointers are welcome. I'm looking forward to
hearing from you ...

Thanks,
--
-- Jun Lu                          Internet:luj@ecn.purdue.edu          --
-- Aeronautics & Astronautics      Bitnet:  luj%ecn.purdue.edu@purccvm  --
-- Purdue University		   UUCP:    pur-ee!luj                  -- 
-- W. Lafayette, IN 47907          Phone:317-494-9410  Fax:317-494-0307 --

henry@zoo.toronto.edu (Henry Spencer) (01/06/91)

In article <1991Jan6.044056.23028@noose.ecn.purdue.edu> luj@delta.ecn.purdue.edu (Jun Lu) writes:
>It would be nice if I can have a "subroutine" which takes arbitrary matrices as
>arguments and performs some operations on them. The dimensions of the 2-d
>array are not known a priori...

Can't be done in C.  The dimensions of an array, with the exception of the
first, must be known at compile time.

(Well, "can't be done" is an exaggeration, but what you end up doing is
cheating, declaring the arguments as pointers and doing the subscript
arithmetic yourself rather than using [] notation.)

This is generally considered a deficiency, but it is harder to solve than
it looks.  Some compilers have extensions to do it, although most of those
extensions have problems of one kind or another.  There is work being done
on finding a good solution.
-- 
If the Space Shuttle was the answer,   | Henry Spencer at U of Toronto Zoology
what was the question?                 |  henry@zoo.toronto.edu   utzoo!henry

rory@maccs.dcss.mcmaster.ca (Rory Jacobs) (01/06/91)

In article <1991Jan6.044056.23028@noose.ecn.purdue.edu> luj@delta.ecn.purdue.edu (Jun Lu) writes:
>It would be nice if I can have a "subroutine" which takes arbitrary matrices as
>arguments and performs some operations on them. The dimensions of the 2-d
>array are not known a priori.  In other words, how do you add two
>matrices together portably in C ? ( see following scenario descriptions )
>
>main()
>{
>     double m1[4][2], m2[4][2], m[4][2];
>     double a[5][5], b[5][5], c[5][5];
>
>     /* init m1, m2 ... */
>
>
>     madd(m1, m2, m, 4, 2);
>     madd(a, b, c, 5, 5);
>
>     /* ... */
>
>}
>
>madd(a, b, c, m, n)
> /* args --- how ???(Note n is not known at compile time, but we assume that
>    the type of the 2-d array elements are known, i.e. double */
>{
>    /*
>     * some work --- how ????
>     */
>
>     for (i = 0; i < m; i++)		/* fucntionality of madd() */
>	for (j = 0; j < n; j++)
>	    c[i][j] = a[i][j] + b[i][j];
>
>}


Use pointer arithemetic, as in the following way:

/*-------------------*/
  void madd(a,b,c,m,n)
/*-------------------*/
double *a,*b,*c;
int m,n;
{
   for (i=0;i<m;i++) {
      for (j=0;j<n;j++) {
         *(c+j+i*n) = *(a +j+i*n) + *(b+j+i*n);
      }     
   }
}

Explaination:  memory is stored sequentially --- including
multi-dimensional arrays.  Since we know that in C they are
stored in row-major form (row-by-row) and that the name of 
a array can be considered a pointer to the array, we can deal
with the pointer.

Calculating addresses from a two-dimensional array:

  --
  | a11  a12     .....    a1n |
  | a21  a22     .....    a2n |
  | ...                       |
  | ai1  ai2  ... aij ... ain |
  | ...                       |
  | am1  am2     .....    amn |
  --                         --


We want to know the number of entries up to and including the 
aij in the array.  This is the number of entries in complete 
rows before the ith row (i-1) * n , and the number of entries 
to the aij (which is j)

   a[i][j] = *(a+(i-1)*n +j)

But now we must remember C indices start at 0, so M ranges from 0 to
m-1, and N from 0 to n-1, ie. the one is already subtracted for you.
thus
   a[i][j] = *(a+i*n+j)

For a more decent treatment of how to calculate addresses (it is
a sunday morning after saturday night) see a half-way-descent 
discrete math book.

   Sure hope this helps,
      Rory Jacobs


Rory Jacobs                                   Who me?!?
rory@maccs.dcss.mcmaster.ca                   Let's go Flyers!
...!uunet!uati!utgpu!maccs!rory               I thought it was easy...
Department of Computer Science and Systems    Boring (yawn)!
McMaster University, Hamilton, Ont            Let's have some fun.

tps@chem.ucsd.edu (Tom Stockfisch) (01/08/91)

In article <1991Jan6.044056.23028@noose.ecn.purdue.edu> luj@delta.ecn.purdue.edu (Jun Lu) writes:
>It would be nice if I can have a "subroutine" which takes arbitrary matrices as
>arguments and performs some operations on them. The dimensions of the 2-d
>array are not known a priori.

I'm so proud of my clever programming constructs for this I just have to
post every time this topic comes up (-:
Using your code with my method for handling 2-d arrays in C,

main()
{
     double m1[4][2], m2[4][2], m[4][2];

     /* init m1, m2 ... */


     madd(m1[0], m2[0], m[0], 4, 2);
     /* ... */
}

madd( a, b, c, m, n )
	double	a[/* m*n */], b[/* m*n */], c[/* m*n */];
#		define A(i,j)	( (i) *n+ (j) )
#		define B(i,j)	( (i) *n+ (j) )
{
	int	i, j;

	for (i = 0; i < m; i++)		/* fucntionality of madd() */
		for (j = 0; j < n; j++)
	    		C(i,j) = A(i,j) + B(i,j);
}

Note the missing declarations for m,n.  This is because they appear in
the comments inside the array dimensions.
Note the macro definitions are analogues of the Fortran "dimension" statement.


>No calling Fortran from C please. 

Certainly not for this.
-- 

|| Tom Stockfisch, UCSD Chemistry	tps@chem.ucsd.edu

davee@hpcllla.cup.hp.com (Dave Elliott) (01/09/91)

I'm just a C novice, so maybe I'm way off base here... but isn't the solution
something like the following:

     madd(a, b, c, m, n)
     double *a, *b, *c;
     int m, n;
        {
         double *end;
         end = a + m * n;
         for ( ; a<end; ++a, ++b, ++c)
            *c = *a + *b;
        }
                      
                                         - Dave Elliott
                                           davee@hpda.hp.com