Article View: comp.lang.c.moderated
Article #2564Re: compatibility
From: Chris Torek
Date: Tue, 09 Apr 1996 22:42
Date: Tue, 09 Apr 1996 22:42
125 lines
5404 bytes
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-mailReferences:
<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