[net.lang.c] Declaring pointer to array of structures, within a structure

levy@ttrdc.UUCP (Daniel R. Levy) (06/03/86)

Hello out there, ye Gurus.

I have a situation:

typedef struct foo {
	char *a;
	char *b;
};

typedef struct bar {
	char *c;
	struct foo *d;	/* Is this right, to point to an ARRAY of struct foos,
				please read on */
};

/* Here's what I'd like to do */

main()
{
	char *malloc();
	struct bar *BAR;
		/* allocate space for one bar: */
	BAR=(struct bar *)malloc((unsigned)sizeof(struct bar));
		/* allocate space for array of ten struct foos and
			have BAR->d point to that space: */
	BAR->d=(struct foo *)malloc((unsigned)(10 * sizeof(struct foo)));
		/* Use members of this array of struct foo, e.g.: */
	(BAR->d)[5].b="This is a test\n";
	/* .... */
	return 0;	/* if I haven't core dumped :-) */
}

Now this seems to work perfectly well, and lint says nary a word about
this code except for a complaint about the possible alignment problem
from the cast of malloc(), which I presume is normal.  But what bugs me
is that in my typedef of bar, I don't say that member d is a pointer to
an ARRAY of struct foo, I just say that it is a pointer to one struct foo.
Is this kosher, or have I violated some subtle but important distinction
between arrays and pointers to them?  Remember, I'd like to be able to
treat member d as I would the name of any other array in referencing
the memory it points to.

When I tried to say d is a pointer to an array of struct foo and tried:

	struct foo *(d[]);
	or
	struct foo (*d)[];

this caused cc to shoot its cookies all over the place later in the code
about illegal lhs when trying to store the pointer I got from malloc()
in BAR->d.  Which makes sense to me, because arrays aren't lvalues.
But still I feel like something is Missing.

I can't seem to find anything in K&R about how to handle this.  Is my code
above OK as is with respect to the declaration of d and subsequent use of
BAR->d or are there more changes which must be made to make the code fit to
be seen by Royalty?

Gurus hither and yon are welcome to offer whatever advice by mail or
posting that they see fit, and adTHANKSvance.
-- 
 -------------------------------    Disclaimer:  The views contained herein are
|       dan levy | yvel nad      |  my own and are not at all those of my em-
|         an engihacker @        |  ployer or the administrator of any computer
| at&t computer systems division |  upon which I may hack.
|        skokie, illinois        |
 --------------------------------   Path: ..!{akgua,homxb,ihnp4,ltuxa,mvuxa,
						vax135}!ttrdc!levy

ark@alice.UucP (Andrew Koenig) (06/03/86)

> Now this seems to work perfectly well, and lint says nary a word about
> this code except for a complaint about the possible alignment problem
> from the cast of malloc(), which I presume is normal.  But what bugs me
> is that in my typedef of bar, I don't say that member d is a pointer to
> an ARRAY of struct foo, I just say that it is a pointer to one struct foo.
> Is this kosher, or have I violated some subtle but important distinction
> between arrays and pointers to them?  Remember, I'd like to be able to
> treat member d as I would the name of any other array in referencing
> the memory it points to.

You're doing the right thing.

In C, the size of an array can only be a constant.  However,
the name of an array is almost always translated to a pointer
to its zeroth element.  Thus you can use a pointer in essentially
all contexts where you might use an array.  The only difference
I can think of is that sizeof(BAR->d) will be the size of a
pointer rather than the amount of memory you allocated.

Incidentally, you don't need to write (BAR->d)[5] because
-> binds more tightly than [] .  You can therefore write BAR->d[5] .

chris@umcp-cs.UUCP (Chris Torek) (06/04/86)

In article <909@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>typedef struct bar {
>	char *c;
>	struct foo *d;	/* Is this right, to point to an ARRAY of struct foos,
>				please read on */
>};

You left out the type name for the typedef, but other than that,
yes, it is correct.

>... But what bugs me is that in my typedef of bar, I don't say
>that member d is a pointer to an ARRAY of struct foo, I just say
>that it is a pointer to one struct foo.  Is this kosher, or have
>I violated some subtle but important distinction between arrays
>and pointers to them?

It is fine.  All a compiler needs to implement an unchecked
one-dimensional array is a base address and an element size;
and a pointer to any `object with size' supplies both.

>Remember, I'd like to be able to treat member d as I would the
>name of any other array in referencing the memory it points to.

The clause `in referencing ...' is important.  Given (e.g.)

	struct bar bar_array[10];

the compiler `knows' how many `bar_array' elements there are.
`sizeof bar_array' returns the number of bytes occupied by the
entire array; and a compiler could insert subscript checking
on accesses to bar_array[i].  However, given

	struct bar *bar_pointer;

the compiler only `knows' that `bar_pointer' is of type `pointer
to struct bar' and that `*bar_pointer' is of type `struct bar';
`sizeof bar_pointer' gives the number of bytes occupied by the
pointer, and a compiler is much harder pressed to come up with
`subscript' checking for bar_pointer[i] (though it *can* be done).

>When I tried to say d is a pointer to an array of struct foo and tried:
>
>	struct foo *(d[]);
>	or
>	struct foo (*d)[];

The second is a `pointer to array of struct foo' in cdecl syntax.
(The first is `array of pointer to struct foo', not what you asked
for.)  In the latter case case the array part carries no number of
elements, which creates a bit of a problem if one writes `d[i]':
to find d[i], the compiler must go to the i'th `d', but how big is
each d[j], 0 <= j < i?  (Answer:  somewhere between zero `struct
foo's and an infinite number of `struct foo's.  This is no help.)
A compiler could still handle (*d)[i], though:  no matter how big
(*d) is, its address is zero bytes away from the address of (*d);
and (*d)[0] is `sizeof (struct foo)' bytes big.  Zero times anything
is zero, so the size of d[0], a.k.a. (*d), is unimportant.

(Actually, I have glossed over something.  The size of (*d) must
be known if different pointers are different sizes, else how is
the compiler to generate the correct dereference instruction?  On
most machines all pointers are the same size, and one can get away
with a generic dereference instruction.  In general, the more
information you feed your compiler---assuming it is correct---the
better.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu