[comp.lang.c] typedefing arrays

spl@mcnc.org (Steve Lamont) (06/05/89)

Gentlefolk:

  The recent discussions about aggregate initializations and so forth
got me to thinking (this is always a dangerous thing) about typedefed
aggregates.  I know that you can typedef something like

	typedef struct {
		float	x;
		float	y;
		float	z;
	} vertex;

and then treat vertex almost in the same way as a primitive type (such
as char, int, float, etc).  F'rinstance, you can allocate an array of
vertex in the following manner

	vertex 	*v;

	v = ( vertex *) malloc( sizeof( vertex ) * some_number );

and do wonderful things with v.

  Now to the nub of my question:

  Is it possible to do something like

	typedef int	vertex[3];

	vertex	*v;

	v = ( vertex *) malloc( sizeof( vertex ) * some_number );

My compiler will swollow it, and, if I play all sorts of funny games
with pointer dereferencing, I can even put numbers into the array v and
pull them out again in some semblance of correct order.  However, all of
the solutions I come up with are inelegant, at best. 

  So, to the question before the house.  Is this a sensible thing to do?
Is it even valid C (as I say, I can get both Microsoft 5.1 and BSD 4.3 C
compilers to swollow it)?  How do I reference individual elements
(v[0], v[1], v[2]) as I am able to in the first instance (v->x, v->y,
v->z)?  Do I have to resort to klugey constructs to use this construction
profitably?  Or should I just forget the whole thing?

-- 
							spl
Steve Lamont, sciViGuy			EMail:	spl@ncsc.org
North Carolina Supercomputing Center	Phone: (919) 248-1120
Box 12732/RTP, NC 27709

gwyn@smoke.BRL.MIL (Doug Gwyn) (06/06/89)

In article <4636@alvin.mcnc.org> spl@mcnc.org (Steve Lamont) writes:
>  Is it possible to do something like
>	typedef int	vertex[3];

Sure; <setjmp.h> is supposed to typedef a jmp_buf like this.

All the other questions are answered by "Use the type the way it
must be used by its very definition and it will work just fine."

bumby@math.rutgers.edu (Richard Bumby) (06/06/89)

In article <4636@alvin.mcnc.org> spl@mcnc.org (Steve Lamont) writes:

> Gentlefolk:
> 
> 		    . . . <stuff omitted> . . .
>
>   Now to the nub of my question:
> 
>   Is it possible to do something like
> 
> 	typedef int	vertex[3];
> 
> 	vertex	*v;
> 
> 	v = ( vertex *) malloc( sizeof( vertex ) * some_number );
> 
> 		  . . . <more stuff omitted> . . .
> 
>   So, to the question before the house.  Is this a sensible thing to do?
> Is it even valid C (as I say, I can get both Microsoft 5.1 and BSD 4.3 C
> compilers to swollow it)?  How do I reference individual elements
> (v[0], v[1], v[2]) as I am able to in the first instance (v->x, v->y,
> v->z)?  Do I have to resort to klugey constructs to use this construction
> profitably?  Or should I just forget the whole thing?

You are really just setting up v as an array, as the following program
illustrates.  

#include <stdio.h>
#define N 5


void *malloc( int );
typedef int     vertex[3];

main()
      {
      vertex  *v;
      int i,j;

      v = ( vertex *) malloc( sizeof( vertex ) * N );
      for ( i = 0 ; i < N ; ++i )
          for( j = 0 ; j < 3 ; ++j )
               v[i][j] = i*i + j;
      for ( i = 0 ; i < N ; ++i )
          printf( "row %3d: %8d %8d %8d\n" , i , v[i][0] ,
                            v[i][1] , v[i][2] );
      }


/* output:


row   0:        0        1        2
row   1:        1        2        3
row   2:        4        5        6
row   3:        9       10       11
row   4:       16       17       18

*/

It seems like good C to me.
-- 

--R. T. Bumby ** Math ** Rutgers ** New Brunswick **
(in one form or another for all kinds of mail)
[bumby@math.rutgers.edu]

chris@mimsy.UUCP (Chris Torek) (06/06/89)

In article <4636@alvin.mcnc.org> spl@mcnc.org (Steve Lamont) writes:
>  Is it possible to do something like
>
>	typedef int	vertex[3];

This creates a type alias called `vertex' which means `array 3 of int'.

>	vertex	*v;

Thus, this declares v as `pointer to array 3 of int', and

>	v = (vertex *) malloc( sizeof( vertex ) * some_number );

this calls malloc with an argument of `sizeof(int [3])*n' and casts
the result to `pointer to array 3 of int'.

>My compiler will swallow it, and, if I play all sorts of funny games
>with pointer dereferencing, I can even put numbers into the array v and
>pull them out again in some semblance of correct order.  However, all of
>the solutions I come up with are inelegant, at best. 

The return from malloc, as cast and stored in v (if not nil), is suitable
for use as an `array n of array 3 of int' (I am using `n' where you have
`some_number').  To talk about the third `vertex', write

	v[2]

(the first vertex is v[0], not v[1]).  This is an object of type
`array 3 of int'; in most contexts, it is transformed into one of type
`pointer to int', which then points to the first of the three `int's.

>Is this a sensible thing to do?

Yes.

>... How do I reference individual elements (v[0], v[1], v[2]) as I am
>able to in the first instance (v->x, v->y, >v->z)?

v[0][0] is the first int in the first array of 3 ints; v[0][1] is the
second int in the first array of 3 ints; v[0][2] is the third int in
the first array of 3 ints.  All three exist if v != NULL and if n was
at least 1.  These can also be written as (*v)[0], (*v)[1], and (*v)[2]
respectively, and the first has yet another form, **v.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

karl@haddock.ima.isc.com (Karl Heuer) (06/06/89)

In article <4636@alvin.mcnc.org> spl@mcnc.org (Steve Lamont) writes:
>Is it possible to do something like
>	typedef int	vertex[3];
>	vertex	*v;
>	v = ( vertex *) malloc( sizeof( vertex ) * some_number );

Yes, provided your compiler supports the concept of pointer-to-array.$%  Your
example would be equivalent to the non-typedef code:
	int (*v)[3];
	v = (int (*)[3])malloc(sizeof(int [3]) * some_number);

In either case, accessing an element is done via (*v)[i].  I presume this is
the syntax that you found "inelegant".  That's a matter of taste, I guess, but
it's exactly analogous to (*fp)(arg) for function pointers@, and (*sp).mem for
struct pointers, the only difference being that this last one is so common
that it has the builtin synonym "sp->mem".

Actually, the above paragraph applies to single-object allocations.  Since
you're allocating a vector (of length some_number) of these vertices, you can
reference the i'th coordinate of the n'th vertex in the vector by simply
writing "v[n][i]".  Looks a lot like a two-dimensional array, right?  Not
surprising, since in fact (int (*)[3]) is the type of the rvalue that results
from a two-d array of type (int [M][3]) after the array decays into a pointer.

Now, if the three elements (despite having a common type) are conceptually
different and noninterchangeable, so you'd always be referencing them with a
constant index, then you should probably stick with the struct definition.  If
it really needs to be an array, and the notation still bothers you (or if you
want to ensure it will port to compilers that don't support array pointers), I
recommend "typedef struct {int coord[3];}", which allows you to reference the
elements with "v->coord[i]" (single-object allocation) or "v[n].coord[i]"
(multiple-object allocation).#

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
________
$  Note that pointer-to-array-of-int is *not* the same thing as pointer-to-
   -int-element-of-array, which is much more common.  The latter is what you
   get when you write "p = &a[i]", or when you let an array-of-int decay into
   a pointer.  The former is a pointer to the *entire array* itself, is a very
   rarely used type, and behaves differently with, e.g., the "++" operator.
%  I believe some older compilers don't support it.  And I know there are many
   that allow you to have a pointer-to-array, but refuse to believe that "&a"
   is a legal way to generate such an object.  (Fixed in ANSI C.)
@  Actually, X3J11 has rewritten the rules so that it's now legal to invoke a
   function via a pointer without explicitly dereferencing: fp(arg) is legal
   in ANSI C.  Personally, I prefer to maintain the distinction between
   functions and function-pointers.
#  This, together with "#define x coord[0]" etc., can be quite useful.

spl@mcnc.org (Steve Lamont) (06/06/89)

In article <Jun.5.17.50.31.1989.803@math.rutgers.edu> bumby@math.rutgers.edu (Richard Bumby) writes:
<In article <4636@alvin.mcnc.org> spl@mcnc.org (Steve Lamont) writes:
<
<> Gentlefolk:
<> 
<> 		    . . . <stuff omitted> . . .
<>
<>   Now to the nub of my question:
<> 
<>   Is it possible to do something like
<> 
<> 	typedef int	vertex[3];
<
<  [question with obvious answer omitted]
<It seems like good C to me.
<-- 
<
Thanks to
<--R. T. Bumby ** Math ** Rutgers ** New Brunswick **
and Doug Gwyn (I think, my apologies if I got the name wrong) for
enlightenment.  My problem was not thinking pointer!


-- 
							spl
Steve Lamont, sciViGuy			EMail:	spl@ncsc.org
North Carolina Supercomputing Center	Phone: (919) 248-1120
Box 12732/RTP, NC 27709

karzes@mfci.UUCP (Tom Karzes) (06/06/89)

In article <4636@alvin.mcnc.org> spl@mcnc.org (Steve Lamont) writes:
>  ...
>  Is it possible to do something like
>
>	typedef int	vertex[3];
>
>	vertex	*v;
>
>	v = ( vertex *) malloc( sizeof( vertex ) * some_number );

Yes, you can do this.  In this case v has type "int (*)[3]", i.e., a
pointer to an array of 3 ints (or a pointer into an array of arrays
of 3 ints).  Your call to malloc should allocate "some_number" objects
of the size of vertex, which is the size of three ints (so the total
size is 3 * "some_number" ints).

To access the 2nd element (index 1) of the 21st vertex (index 20) in v,
you could write v[20][1].  Here, v[20] gets you the 21st vertex, and
v[20][1] gets you the 2nd element of that vertex.  With the structure
version you showed in your original message, if the second element of
a vertex corresponds to y, you would have written v[20].y instead.

If you add or subtract integers to or from v, it will skip past whole
instances of vertex.  I.e., (*(v + 5))[i] is the same as v[5][i].
However, beware of precedence.  The following are all equivalent:

    v[i][j]
    (*(v + i))[j]
    *(v[i] + j)
    *(*(v + i) + j)

But *(v + i)[j] is equivalent to *((v + i)[j]) which is equivalent to
*(*((v + i) + j)) or **((v + i) + j) or **(v + (i + j)) or *v[i + j]
or v[i + j][0].

If you get confused, it may help to think of how the case where vertex
is wrapped in a struct works.  I.e.,

    typedef struct {
        int     p[3];
    } vertex;

    vertex *v;

    v = (vertex *) malloc(sizeof(vertex) * some_number);

Then v[20][1] in the previous example becomes v[20].p[1] in this example,
and the following are all equivalent:

    v[i].p[j]
    (*(v + i)).p[j]
    (v + i)->p[j]
    *(v[i].p + j)
    *((*(v + i)).p + j)
    *((v + i)->p + j)

Note that in the second and fifth cases above your compiler would give
you a syntax error if you left off the parentheses around (*(v + i)).

awd@dbase.UUCP (Alastair Dallas) (06/11/89)

In article <17895@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes:
> In article <4636@alvin.mcnc.org> spl@mcnc.org (Steve Lamont) writes:
> >  Is it possible to do something like
> >
> >	typedef int	vertex[3];
> >
> >Is this a sensible thing to do?
> 
> Yes.
> 

Of course it is.  For example, I like:

typedef char FNAME[<system-dependent size>];

Then you can have things like:

	func(FNAME name, int arg)
	{

		FNAME temp;

		strcpy(temp, name);
		if (temp[1] == ':')
			/* drive specified */
			;

		if (!strcmp(temp, name))
			;

	...and so on...

There are lots of good uses for typedef'd arrays.

/alastair/