[net.lang.c] 'struct' - declarations & parameters

ok@edai.UUCP (Richard O'Keefe) (02/17/84)

     A couple of days ago I had occasion to port a large C program  from
a VAX to an Orion.  The C compiler on the Orion was not based on the PCC
but was derived directly from the book and the V7 addendum,  then hacked
until all the 4.1bsd programs were correctly compiled.

     A note and a question.

     The note is that they have two stacks (like the C machine  and  the
RISC chip) : one for scalars and one for arrays.  'struct' arguments are
passed on the array stack, and struct values returned there.  I can find
nothing in the rather sketchy descriptions of C available to forbid this.
Consequently,

	struct {int a, b, c;} foo = {1, 2, 3};

	main () { printf("%d %d %d\n", foo); }

which works just fine on a VAX, doesn't work on  an  Orion.   I  suspect
that it may not work on a RISC chip or on a C70 either.   Not  only  are
structs passed on the array stack.  So are unions.  I found out the hard
way  on  the  VAX that even when a union fits into 32 bits the PCC still
treats it as a "big" object, so the Orion C compiler  is  not  alone  in
treating  unions  differently  from integers etc.  The result of that is
that
	char xc = 12, *pc = &xc;
	double xd = 19.7, *pd = &xd;
	
	void bother(x, i)
	    union {char *c; double *d;} x;
	    int i;
	    {
		if (i) {
		    printf("%d ", *x.c);
		} else {
		    printf("%g\n", *x.d);
		}
	    }

	void main()
	    {
		bother(pc, 1);
		bother(pd, 0);
	    }

doesn't work either.  pc and pd are  pointers,  so  get  passed  on  the
scalar  stack,  while x is a union, so is looked for on the array stack.
Once again, this doesn't seem to be forbidden by available  descriptions
of C, and is likely to happen on other "C machines".

     The  question  is  whether  the  final  semicolon  in  a   'struct'
declaration  is  optional  or  not.   I  have been using two layouts for
structures:

	typedef struct foo
	    {
		type1 field1;	/* rem1 */
		...
		typen fieldn;	/* remn */
	    }	foo;

for important types; and for tiny little structs embedded in something:

	struct {type1 field1; ...; typen fieldn} dummy;

both of which the VAX C compiler is perfectly happy with.  But the Orion
compiler, being written from the book, insists on the amazingly ugly  ;}
in both cases.  Which compiler is right?

chris@umcp-cs.UUCP (02/21/84)

Well first off, writing

	struct { int i, j, k; } foo = { 1, 2, 3 };

	main () { printf ("%d %d %d\n", foo); }

is just plain illegal.  The bit about unions is also illegal.  If you
must pass a union, do something like

	union xyzzy { char *c; int i; };
	main () {
		char *s;
		...
		{
			union xyzzy temp;
			temp.c = s;
			strange (temp, 1);
		}
		...
	}

(This should be lots of fun for me: Gosling Emacs uses that union
trick for the first parameter to DefMac.  *Sigh*)

As to whether the trailing semicolon is required in structure
declarations:  all I can say is it doesn't hurt to use it.  (Though
if you want type declarations to match function declarations, it
should be optional.  I don't have to write "dummy () { ; }".)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris.umcp-cs@CSNet-Relay

keesan@bbncca.ARPA (Morris Keesan) (02/22/84)

----------------------------

    It looks like the Orion compiler wins in all three cases.  The last case is
the simplest.  The C Reference Manual is very clear that the declaration for
each member of a structure requires a ';' at the end of it, so in this case the
Orion compiler is clearly right, and the VAX compiler you're using is in
violation of the language definition.

    The other two cases are not quite so straightforward.  Both compilers are
behaving in completely legal fashion, since the language definition says nothing
about how argument passing is to be implemented (except that the semantics are
"call by value").  The source code is at fault here, since it was written to
expect the implementation of the calling sequence which is provided by the
VAX compiler (and similar compilers).
    Passing a structure as if it were three integers runs you right into the
"varargs" problem.  From page 71 of K&R:

	By the way, there is no entirely satisfactory way to write a
    portable function that accepts a variable number of arguments, because
    there is no way for the called function to determine how many arguments
    were passed to it in a given call. . . .
	It is generally safe to deal with a variable number of arguments if
    the called function doesn't use an argument which was not actually
    supplied, and if the types are consistent.  printf . . . fails badly if
    the caller does not supply enough arguments or if the types are not
    what the first argument says.

    This suggests that     
>	struct {int a, b, c;} foo = {1, 2, 3};
>	main () { printf("%d %d %d\n", foo); }
is bogus and should never have been coded that way.  Your second example,
passing pointers as if they were unions, is clearly illegal because of type
mismatch between the formal and actual parameters.  Lint should have
complained about this.  Assuming you know enough about the target machine
and the compiler implementation to know that (char *) and (double *) are
the same size, and that a union of the two is also that size, the slightly
more portable way of coding your second example would be

    union foo {char *c; double *d;};

    void bother(x,i)
	union foo x; int i;
	{ . . .
	}

    void main()
        {
	    bother((union foo)pc, 1);
	    bother((union foo)pd, 0);
        }

but this is still non-portable and not guaranteed to work (if, for example,
char pointers are a different size from other pointers).

    Incidentally, both of these rather bogus code samples work okay on a C70,
although the second one won't work with the "void" declarations until my newer
version of the compiler is released.
-- 
					Morris M. Keesan
					{decvax,linus,wjh12,ima}!bbncca!keesan
					keesan @ BBN-UNIX.ARPA

decot@cwruecmp.UUCP (Dave Decot) (02/22/84)

Chris Torek says:

    ...writing

	struct { int i, j, k; } foo = { 1, 2, 3 };

	main () { printf ("%d %d %d\n", foo); }

    is just plain illegal.
    
This code is NOT illegal, nor is it illegal to pass unions.  It is only
non-portable (but lint(1) has no complaint, because the problem is in
printf).  In fact, it usually works (for example, on our VAX, 4.1bsd).  Some
recent changes to C allow you to pass structures and unions as parameters to
functions.  If your compiler has no `enum' types, then it is outdated, and
probably doesn't support struct/union parameter passing.

    As to whether the trailing semicolon is required in structure
    declarations:  all I can say is it doesn't hurt to use it.  (Though
    if you want type declarations to match function declarations, it
    should be optional.  I don't have to write "dummy () { ; }".)

It isn't, and should not be, optional.  The VAX compiler doesn't signal this
as a syntax error, but it should, since it is.  The fact that you need no
semicolon in a dummy function body is no demonstration of inconsistency,
it is a demonstration of consistency.  Statements in C are *terminated*
(not separated, as in Pascal) by a semicolon.  It is only natural that every
declaration (including field declarations) must also be terminated by a
semicolon.

The function body in

    dummy () { ; } 

is allowed and interpreted as a null statement, but in

    dummy () { }

there is no statement (since none are required), and thus no ; is needed.

Dave Decot		 "Non-Americans are people, too."
decvax!cwruecmp!decot    (Decot.Case@rand-relay)