[comp.lang.c] Variable dimensioning in fortran

g-rh@cca.CCA.COM (Richard Harter) (06/18/88)

In article <5917@aw.sei.cmu.edu> firth@bd.sei.cmu.edu.UUCP (Robert Firth) writes:

>The following code, contributed by a C programmer, allocates dynamic
>memory for a two-dimensional array:

>>                                 For this particuliar
>> data structure, the subroutine is basically a one-liner:

>> double **Create2DArray(w,h)
>> int w,h;{ double **r;
>> for(r=(double**)calloc(h,sizeof(*r));h-->0;r[h]=(double*)calloc(w,sizeof(**r)));
>> return(r);}

>Any Fortran programmer who seriously proposes to convert to C would, in
>my opinion, be advised to study this example very carefully.  Verbum
>sapienta sufficit.

	This little trick is all very well, but it does not reproduce the
fortran facility for variable dimensioning, and it does matter.  The above
yields an array of arrays.  Consider the following:

	real a(2,5)
	call foo(a)
	....
	subroutine foo(a)
	real a(10)
	...

In this example a is originally allocated as an array of 10 contiguous
locations; subroutine foo takes advantage of that knowledge.  The point
of fortran subscripting rules is that the dimension structure can be
changed dynamically.
-- 

In the fields of Hell where the grass grows high
Are the graves of dreams allowed to die.
	Richard Harter, SMDS  Inc.

leo@philmds.UUCP (Leo de Wit) (06/21/88)

Sorry this one's so long, I got a bit carried away 8-):
                                  --- -------

In article <29605@cca.CCA.COM> g-rh@CCA.CCA.COM.UUCP (Richard Harter) writes:
>In article <5917@aw.sei.cmu.edu> firth@bd.sei.cmu.edu.UUCP (Robert Firth) writes:
>>> double **Create2DArray(w,h)
>>> int w,h;{ double **r;
>>> for(r=(double**)calloc(h,sizeof(*r));h-->0;r[h]=(double*)calloc(w,sizeof(**r)));
>>> return(r);}
>
>>Any Fortran programmer who seriously proposes to convert to C would, in
>>my opinion, be advised to study this example very carefully.  Verbum
>>sapienta sufficit.
>
>	This little trick is all very well, but it does not reproduce the
>fortran facility for variable dimensioning, and it does matter.  The above
>yields an array of arrays.  Consider the following:

The above C code is not adequate for handling flat arrays; the 2 * 5
elements should be allocated as one chunk, with the r[0], r[1], ...
pointing into it, as I explained in a previous article. I'll repeat
that code here for clearness (and removed the errors; this one will
work 8-):

double **Create2DArray(w,h)
int w,h;
{
    double **r, *a;

    a = (double *)calloc(w * h, sizeof(double));
    r = (double **)calloc(h,sizeof(double *));
    for ( ; --h >= 0; r[h] = a + w * h) ;
    return r;
}

This approach has, besides the array being contiguous, as a benifit
that only two allocations are needed. The array can be handled both by
vectoring:  r[0], r[1], ... and by using the flat array pointed to by
r[0]. Now for your example:

>	real a(2,5)
>	call foo(a)
>	....
>	subroutine foo(a)
>	real a(10)
>	...
>
>In this example a is originally allocated as an array of 10 contiguous
>locations; subroutine foo takes advantage of that knowledge.  The point
>of fortran subscripting rules is that the dimension structure can be
>changed dynamically.

And so for C subscripting rules. Watch me:

extern double **Create2DArray();

static void foo1(), foo2(), foo3(), fooall();

main()
{
    double **a = Create2DArray(2,5);
    int i;

    foo1(a[0]); foo2(a[0]); foo3(a[0]); fooall(a[0]);
    for (i = 0; i < 10; i++) {
        printf("%d: %f\n",i,a[0][i]);
    }
}

static void foo1(a) /* now using it as a 1 dim array : a[10] */
double *a;
{
    a[7] = 3.0;
}

static void foo2(a) /* now using it as a 2 dim array : a[2][5] */
double (*a)[5];
{
    a[1][3] = 45.3;
}

static void foo3(a) /* now using it as a 3 dim array : a[2][2][2] */
double (*a)[2][2];
{
    a[1][1][0] = 2.0;
}

static void fooall(a) /* Full Organ: C major: the previous altogether */
double *a;
{
    double *a1 = a; /* just for symmetry */
    double (*a2)[5] = (double (*)[5])a;
    double (*a3)[2][2] = (double (*)[2][2])a;

    a1[7] = 3.0;
    a2[1][3] = 45.3;
    a3[1][1][0] = 2.0;
}

Like to see you do the last trick in Fortran! Note that it is both
elegant and efficient (although a Fortran programmer will have trouble
reading the declarations, the rest of the code is straightforward); the
same array can be accessed with whatever dimension you like.  The only
trouble is that even experienced C programmers often don't know this
stuff, mostly because they didn't need it.

If you would also like to have the vectors available, pass a itself as
parameter, and use a[0], a[1] for vectors; for example, the header of
fooall() would then be:

static void fooall(a) /* Full Organ: C major: the previous altogether */
double **a;
{
    double *a1 = a[0]; /* just for symmetry */
    double (*a2)[5] = (double (*)[5])a[0];
    double (*a3)[2][2] = (double (*)[2][2])a[0];

If you don't need the vectors (and probably you won't, seeing the
above), Create2DArray could be further simplified; the r[h] vectors are
left out and Create2DArray reduces to a macro:

#define Create2DArray(w,h) (double **)calloc((w)*(h),sizeof(double))

and then, why not, to avoid difficult declarations:

#define Declare1DAcast(a1,a)     double *a1 = (a)
#define Declare2DAcast(a2,a,m)   double (*a2)[(m)] = (double (*)[(m)])(a)
#define Declare3DAcast(a3,a,m,n) double (*a3)[(m)][(n)] = \
                                                     (double (*)[(m)][(n)])(a)
etc. Enjoy!

    Leo.

                        (C me, feel me, touch me, heel me).

g-rh@cca.CCA.COM (Richard Harter) (06/22/88)

In article <517@philmds.UUCP> leo@philmds.UUCP (L.J.M. de Wit) writes:

	... Re previous discussion of a proposed substitute for
	fortran subscripting


]The above C code is not adequate for handling flat arrays; the 2 * 5
]elements should be allocated as one chunk, with the r[0], r[1], ...
]pointing into it, as I explained in a previous article. I'll repeat
]that code here for clearness (and removed the errors; this one will
]work 8-):

]double **Create2DArray(w,h)
]int w,h;
]{
]    double **r, *a;
]
]    a = (double *)calloc(w * h, sizeof(double));
]    r = (double **)calloc(h,sizeof(double *));
]    for ( ; --h >= 0; r[h] = a + w * h) ;
]    return r;
]}

]This approach has, besides the array being contiguous, as a benifit
]that only two allocations are needed. The array can be handled both by
]vectoring:  r[0], r[1], ... and by using the flat array pointed to by
]r[0]. Now for your example:

	Sundry examples omitted.  The main point of the examples is
that any static dimensioning desired can be set up by properly casting
pointers.  This is quite correct.  The thing that cannot be done in C
is to have variable dimensioning using C subscripting (as many people
have pointed out you can fake it with a macro which does the address
calculation.)

	Offhand, my feeling is that I wouldn't use any of these schemes.
C isn't fortran and vice versa.  C doesn't allow 

	foo(m,n,a)
		int m,n;
		double a[m][n];
	{....}

and that's the way it is.  Cute tricks to simulate this just make the
code obscure.  
-- 

In the fields of Hell where the grass grows high
Are the graves of dreams allowed to die.
	Richard Harter, SMDS  Inc.

rrr@naucse.UUCP (Bob Rose ) (06/22/88)

In article <517@philmds.UUCP>, leo@philmds.UUCP (Leo de Wit) writes:
> I'll repeat that code here for clearness (and removed the errors;
> this one will work 8-):
> 
> double **Create2DArray(w,h)
> int w,h;
> {
>     double **r, *a;
> 
>     a = (double *)calloc(w * h, sizeof(double));
>     r = (double **)calloc(h,sizeof(double *));
>     for ( ; --h >= 0; r[h] = a + w * h) ;
>     return r;
> }

Close, but ...  I assume you are using calloc to zero the array, but
the whole world is not a VAX. Try:

double **Create2DArray(w,h)
register int w,h;
{
    register double **r, *a, **q;
    register int i;

    if (((a = (double *)malloc(i = w*h, sizeof(double))) == 0) ||
        ((r = (double **)malloc(h,sizeof(double *))) == 0))
            abort();
    /*
     * Zero the array.
     */
    for (*r = a; i--; *a++ = 0.0) ;
    for (q = r, a = *r; --h; *++q = (a += w)) ;
    return r;
}

Just fuel for the fire.
                            -bob

leo@philmds.UUCP (Leo de Wit) (06/26/88)

In article <749@naucse.UUCP> rrr@naucse.UUCP (Bob Rose ) writes:
>In article <517@philmds.UUCP>, leo@philmds.UUCP (Leo de Wit) writes:
>> [my example using calloc() deleted]
>Close, but ...  I assume you are using calloc to zero the array, but
>the whole world is not a VAX. Try:
>
> [Bob's example using malloc() deleted]

What makes you think calloc() is VAX-specific? I checked with K&R:
section 7.9 Storage Management contains calloc(); with Lattice C:
had also calloc(). Now sure my ST is fast but would you call it a
VAX??  Ultrix has calloc (see man 3 malloc) (and |Ultrix - BSD| < eps);
VAX-VMS has calloc (now there's a VAX); on my work calloc is in the
ansi.h header file we use for portability (so it should be in the ANSI
draft):

    extern void *calloc(size_t nmemb, size_t size);

I think you were mistaken with cfree() which is not as general (VAX-VMS
has it).

>Just fuel for the fire.
>                            -bob

Thanks! So I can light my cigar now 8-).

                   Leo++

gwyn@brl-smoke.ARPA (Doug Gwyn ) (06/27/88)

In article <527@philmds.UUCP> leo@philmds.UUCP (L.J.M. de Wit) writes:
>In article <749@naucse.UUCP> rrr@naucse.UUCP (Bob Rose ) writes:
>>In article <517@philmds.UUCP>, leo@philmds.UUCP (Leo de Wit) writes:
>>> [my example using calloc() deleted]
>>Close, but ...  I assume you are using calloc to zero the array, but
>>the whole world is not a VAX. Try:
>What makes you think calloc() is VAX-specific?

It's true that calloc() exists universally, but its function is to allocate
memory and initialize it with 0 BYTE data.  That does not in general
properly initialize all data types (particularly floating-point and
pointer types), thus the necessity of malloc() followed by an explicit
initialization loop.  On a VAX this use of calloc() typically happens to
work, by accident.

friedl@vsi.UUCP (Stephen J. Friedl) (06/27/88)

In article <8168@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes:
> >What makes you think calloc() is VAX-specific?
> 
> It's true that calloc() exists universally, but its function is to allocate
> memory and initialize it with 0 BYTE data.  That does not in general
> properly initialize all data types (particularly floating-point and
> pointer types), thus the necessity of malloc() followed by an explicit
> initialization loop.  On a VAX this use of calloc() typically happens to
> work, by accident.

This has been my approach to a Q&D allocator for some
kind of arbitrary data type:

/*
 * objalloc()
 *
 *	Return one initialized item.
 */

typedef struct object	OBJ;

OBJ *
objalloc()
{
static OBJ dummy;	/* guaranteed to be "the right" zeros	*/
OBJ	   *p;

	p = (OBJ *)Malloc(sizeof(*p));	/* does error checking internally */

	*p = dummy;			/* do a *real* initialization	  */

	return(p);
}


-- 
Steve Friedl     V-Systems, Inc. (714) 545-6442     3B2-kind-of-guy
friedl@vsi.com     {backbones}!vsi.com!friedl    attmail!vsi!friedl

Nancy Reagan on the Free Software Foundation : "Just say GNU"

sbw@naucse.UUCP (Steve Wampler) (06/27/88)

From article <527@philmds.UUCP>, by leo@philmds.UUCP (Leo de Wit):
> In article <749@naucse.UUCP> rrr@naucse.UUCP (Bob Rose ) writes:
>>Close, but ...  I assume you are using calloc to zero the array, but
>>the whole world is not a VAX. Try:
>>
>> [Bob's example using malloc() deleted]
> 
> What makes you think calloc() is VAX-specific? I checked with K&R:

.
I'm pretty sure Bob was not worried about calloc() existing, just that
it may do the wrong thing.  The bit pattern for 0.0 is not all 0 bits
on all machines.  What's needed is an falloc()  (filled alloc()), where
the value to fill (and the size, I suppose) are passed as arguments.
.
.
.
.
.(sigh, inews fodder)
-- 
	Steve Wampler
	{....!arizona!naucse!sbw}

rrr@naucse.UUCP (Bob Rose ) (06/28/88)

In article <527@philmds.UUCP>, leo@philmds.UUCP (Leo de Wit) writes:
> I write:
> > Leo de Wit writes:
> > > [my example using calloc() deleted]
> >Close, but ...  I assume you are using calloc to zero the array, but
                                                             ^^^^^
The array was of double!!!!!
> >the whole world is not a VAX. Try:
> >
> > [Bob's example using malloc() deleted]
> 
> What makes you think calloc() is VAX-specific? I checked with K&R:
> [ ... its in all the man pages and dpANS ...  ]

You misunderstood me. (A lot of people have been doing that lately,
maybe I need a vacation :-) Calloc is available every where except on
systems totally brain damaged. The thing is calloc binary zero's the
memory. The array you where zeroing was of type double. Now no where
does it say that the internal representation of a double must be binary
zero (most machines it is, but NOT all.) This turns out to be one of
those hard to find bugs when porting to an odd ball architecture.

> >Just fuel for the fire.
> Thanks! So I can light my cigar now 8-).
> 
>                    Leo++

So did I make myself clear or should I buy you a box of cigars for the
long battle ahead :^)
                     --bob

P.S. I would sure like a chapter and vers if calloc can be used to
create an array of zeroed doubles.

leo@philmds.UUCP (Leo de Wit) (06/30/88)

In article <760@naucse.UUCP> rrr@naucse.UUCP (Bob Rose ) writes:
  [calloc stuff deleted]...
|You misunderstood me. (A lot of people have been doing that lately,
|maybe I need a vacation :-) Calloc is available every where except on
|systems totally brain damaged. The thing is calloc binary zero's the
|memory. The array you where zeroing was of type double. Now no where
|does it say that the internal representation of a double must be binary
|zero (most machines it is, but NOT all.) This turns out to be one of
|those hard to find bugs when porting to an odd ball architecture.

As a matter of fact, the calloc() came from someone else's article.
That person used a calloc for each row of the array and a calloc for
the array of pointers to the rows. I didn't even bother to change them
to malloc's which I use myself normally; perhaps to make the comparing
easier (how's that for an excuse ? ;-).

As for initialization, I think it is better to have that as a separate
function; there may just be cases in which it is inefficient to first
set all values to zero, then fill it with values.

|| |Just fuel for the fire.
|| Thanks! So I can light my cigar now 8-).
|| 
||                    Leo++
|
|So did I make myself clear or should I buy you a box of cigars for the
|long battle ahead :^)
|                     --bob

Make it a pipe of peace. And now you did it (to) yourself:
you made yourself clear using calloc 8-)
(but that's allowed in this case, you being an integer person, not some
double-crosser %-).

                    !!leo