[net.lang.c] structure references in header files

txlutera@crdc.arpa (Thomas Luteran) (12/11/85)

I have a problem with a header file that contains typedefs similar
to those below in that both contain a reference to the other.  The
problem stems from the fact second structure will be undefined
(not properly defined) when the first typedef tries to reference it: 

typedef struct car {
    int   numdoors;
    char  color[8];
    char  owner[20];
     .
     .
     .
    struct garage *parked;      <- using LOT here for struct garage won't 
    struct car    *next;
    } AUTO;		           work!			

typedef struct garage {
    char  location[20];
     .
     .
     .
    struct car *customers;
    struct garage *next;
    } LOT;

Please  send any replies directly to me  - Thanks in advance!

					Tom

gwyn@BRL.ARPA (VLD/VMB) (12/11/85)

The approved way to declare two structs each of which has a
member that is a pointer to the other type of struct, is:

struct a;

struct b {
	struct *a;
	};

struct a {
	struct *b;
	};

Yes, I know it looks unbelievable.  This kludge is present
precisely to handle this kind of problem.

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (12/11/85)

OOPS!  Please ignore my previous posting on this, which
had the member types misdeclared.  The idea was right but
I was distracted while typing it.  Here is the correct version:


The approved way to declare two structs each of which has a
member that is a pointer to the other type of struct, is:

struct a;

struct b {
	struct a *ap;	/* BUG FIX */
	};

struct a {
	struct b *bp;	/* BUG FIX */
	};

Yes, I know it looks unbelievable.  This kludge is present
precisely to handle this kind of problem.

chris@umcp-cs.UUCP (Chris Torek) (12/12/85)

Ai!  Doug, how could you?  You posted an example that is not even
syntactically correct!

C compilers are obliged by K&R to accept declarations of pointers
to structure types even if those structure types have not yet been
declared.  That is, the following is legal:

	/*
	 * Doubly linked list of node headers
	 */
	struct nodehead {
		struct	nodehead *nh_next;
		struct	nodehead *nh_prev;
		struct	node *nh_front;		/* <1> */
	};

	/*
	 * Singly linked nodes with backpointers to the node header
	 */
	struct node {
		struct	node *n_next;
		struct	nodehead *n_head;	/* <2> */
		.
		.
		.
	};

If you intend to use anonymous structures and typedefs, e.g.,

	typedef struct {
		...
	} NODEHEAD;

you still need a name for at least one of the two structures, as
C compilers are allowed to be `one pass' and cannot infer that a
name is a typedef that has yet to be compiled.  At least one of
the two marked references (<1> and <2>) must be to a `struct foo *',
since at least one will be a forward reference no matter how you
organise the declarations.

I would check my X3J11 draft to see what it says, but someone
`cleaned up' my desk.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

draves@harvard.UUCP (Richard Draves) (12/12/85)

In general it is important to include the 'forward declaration':

	struct a;
	struct b { struct a *pa; };
	struct a { struct b *pb; };

This makes a difference when the declarations are inside a scope
that already can see a definition of 'struct a'.  For instance,

	struct a { ... };
	main()
	{
		struct b { struct a *pa; };
		struct a { struct b *pb; };
		...
	}

does not do what one might expect.

Rich
-- 

	"a picture in the head is a gory murder in an art gallery"

					-- Stephen Kosslyn

mouse@mcgill-vision.UUCP (der Mouse) (12/13/85)

> I have a problem with a header file that contains typedefs similar
> to those below in that both contain a reference to the other.

> [example, similar to:]
> typedef struct a { struct b *b; } A;
> typedef struct b { struct a *a; } B;

     Well, what I would do is

	struct a {
	  struct b {
	    struct a *a; } *b; };

	typedef struct a A;
	typedef struct b B;

     I  don't believe you can  make  it recognize forward references  in
general.  You certainly  can't  do it for direct  elements,  because the
compiler doesn't know  the size of the  member, and you  can't do it for
pointers because architectures might (do?) exist out there for which not
all pointers are the same size (yes, I too think anyone who designs such
a machine as "general purpose" should be forced to write the C  compiler
for it  themselves; it  might teach  them something).  So you  certainly
can't forward reference the typedefs.   On  some  machines you  might be
able to  forward reference the structure, provided you use a  pointer to
it (does  X3J11  have anything to say  about this?).   Someone mentioned
that a kludge had been put in so you could get around this with

	struct b;
	struct a { struct b *b; };
	struct b { struct a *a; };

     Ugh.

     Speaking of structures containing self-referential pointers, I once
wrote some code which used the following structures:

	struct _coord {
	  struct _coord *flink;
	  struct _coord *blink;
	  int val;
	  int index;
	  struct _boxlist {
	    struct _boxlist *link;
	    struct _box {
	      struct _box *flink;
	      struct _box *blink;
	      int number;
	      struct _coord *lx;
	      struct _coord *ux;
	      struct _coord *ly;
	      struct _coord *uy; } *box; } *boxes; };

     Three  structures, a total of 14 structure elements, and only three
elements which aren't just a pointer to another structure.

     (Okay, okay, I'll put it in net.jokes.c next time, I promise...)
-- 
					der Mouse

USA: {ihnp4,decvax,akgua,etc}!utcsri!mcgill-vision!mouse
     philabs!micomvax!musocs!mcgill-vision!mouse
Europe: mcvax!decvax!utcsri!mcgill-vision!mouse
        mcvax!seismo!cmcl2!philabs!micomvax!musocs!mcgill-vision!mouse

Hacker: One who accidentally destroys /
Wizard: One who recovers it afterward

geoff@desint.UUCP (Geoff Kuenning) (12/14/85)

People who want to write maximally portable should also note the following
technique, so that they can avoid it:

    typedef struct foo *ZAP;
    typedef struct bar *ZOW;

    struct foo
	{
	ZOW *zowie;
	};

    struct bar
	{
	ZAP zapped;
	};

Since pcc (and, I believe, the original pdp-11 compiler) treats typedefs
as macros, this works on those compilers.  However, it breaks lint, and
it breaks on many compilers that actually treat typedefs as types, since
the structs are undefined at the time of the typedef statement.
-- 

	Geoff Kuenning
	{hplabs,ihnp4}!trwrb!desint!geoff