🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.c.moderated
Article #2564

Re: compatibility

#2564
From: Chris Torek
Date: Tue, 09 Apr 1996 22:42
125 lines
5404 bytes
In article <4kbebh$eid@solutions.solon.com> I wrote:
>>C uses `name equivalence', but C also has separate compilation,

In article <4kclti$ong@solutions.solon.com>
Kazimir Kylheku <c2a192@ugrad.cs.ubc.ca> adds:
>Not quite. C uses name equivalence for struct and union components
>of type expressions.

I think I know what you mean here: specifically, that the other
`constructed' types (e.g., `pointer to array 10 of char') use
structural equivalence.  This is true enough, but I think is not
really the best way to understand it.

It might be more accurate simply to point out that there is no `name'
(or only one, the `constructed' name) for this type.  If the type is
`char (*)[10]', its type is, well, `char (*)[10]'.

>A new typedef name does not introduce a new type.

Yes, but (as I noted) this is unusual in language design.  Usually,
if you say `TYPE a = INTEGER' and `TYPE b = INTEGER', types a and
b are different, even though they are clearly `the same' underneath.
In C, if you write:

	typedef char (*some_name)[10];

you have made `some_name' merely an alternative name (often called
an `alias') for the existing type whose `true name' is `char (*)[10]'.

>A new structure or union _tag_ makes a unique structure or union type.

This is not strictly accurate either.  It is the structure definition,
not the tag, that makes the new type:

	struct foo { int x; int y; } one;
	struct { int x; int y; } two;

The variable named `two' has a type whose `true name' is a struct
with no tag.  No other variables of this type can ever be declared
(though a separate source file can declare a type and/or variables
that have compatible type!).

Furthermore, if you write:

	typedef struct { int x; int y; } my_name;

you have merely created an alternative name for this unnamed struct
tag.  The `true name' of this type is un-name-able (it is a `struct'
with no tag, as before), but the alias provides a way to name the
unnameable.  A structure whose type is the type that `my_name' is
an alias for (whew!) is not compatible with a `struct foo' such as
`one', nor is it type-compatible with the variable `two' (whose type
is completely unnameable), at least not in a single compilation unit.

Ultimately, of course, the way it `really works' in a C compiler
is that each type is allocated in some sort of structure or table
or something.  We can imagine a simplified version that looks
like this:

	struct c_type {
		enum {
			ct_prim,	/* primitive type, e.g., int */
			ct_array,	/* array N */
			ct_ptr,		/* pointer */
			ct_stun		/* structure or union */
		} ct_type;
		union {
			int type_number;	/* if prim or stun */
			unsigned array_size;	/* if array */
			unsigned member_off;	/* if memb */
		} ct_un;
		/*
		 * If the type is an array, ptr, or st/un, we need
		 * to know what it is an array of, ptr to, or contains.
		 */
		struct c_type *ct_next;	/* if array, ptr, or st/un */
	};

	/* Exercise for the reader: figure out what is missing.
	   Think about const and volatile, and scoping. :-) */

A typedef just points the new type-alias to the existing `struct
c_type', or copies these fields (we would copy, rather than point
to, if we had a `char *ct_name' field, for instance).  A struct or
union defininition -- that is, a struct/union keyword, followed by
an optional tag, followed by an open brace, or a struct/union
keyword, followed by a tag that has not yet been defined -- allocates
a new `struct c_type' and gives it the next `type_number', e.g.,

	struct c_type *
	new_stun() {
		static int nextstun;
		struct c_type *ct;

		ct = emalloc(sizeof *ct);
		ct->ct_type = ct_stun;
		ct->ct_un.type_number = nextstun++;
		ct->ct_next = NULL;
		return ct;
	}

and two structure types are compatible iff ct1->ct_un.type_number ==
ct2->ct_un.type_number.

This also handles the rather tricky case of a type whose name goes out
of scope, including type names whose scope is only a prototype, such as:

	extern void f(struct { long l; } *);

Here we have defined a new (unnamed) structure type (whose `true type'
is whatever nextstun++ was), not given it a tag, not given it a typedef,
and then cut off all possible access to the already-inaccessible name
by ending the scope in which it was defined.  This example is pointless,
but it illustrates what happens with:

	extern void g(struct S *);

If the name `struct S' has not been mentioned before, `S' is a new
tag, and the compiler gets a `new_stun' for it.  The scope of this
`struct S' tag ends at the end of the prototype, so that while
`struct S' is a perfectly good name, you can no longer use it to
get at this particular `ct->ct_un.type_number'.
-- 
In-Real-Life: Chris Torek, Berkeley Software Design Inc
El Cerrito, CA	Domain:	torek@bsdi.com	+1 510 234 3167
Technical Details
Message-ID: <4kep3q$8fo@solutions.solon.com>
Path: arc1-ca.pugleaf.net!solon.com!not-for-mail
References: <4jtt2c$3eq@solutions.solon.com> <4k5vop$9ub@solutions.solon.com> <4kbebh$eid@solutions.solon.com> <4kclti$ong@solutions.solon.com>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!solon.com!not-for-mail
From: Chris Torek <torek@elf.bsdi.com>
Newsgroups: comp.lang.c.moderated
Subject: Re: compatibility
Date: 9 Apr 1996 17:42:02 -0500
Organization: Berkeley Software Design, Inc.
Sender: clc@solutions.solon.com
Approved: clc@solutions.solon.com
Message-ID: <4kep3q$8fo@solutions.solon.com>
References: <4jtt2c$3eq@solutions.solon.com> <4k5vop$9ub@solutions.solon.com> <4kbebh$eid@solutions.solon.com> <4kclti$ong@solutions.solon.com>
Reply-To: torek@bsdi.com
NNTP-Posting-Host: solutions.solon.com