This has nothing to do with strength of typing. A C typedef name isn't
a new type; it is a name for a type.
The type is constructed by the syntax of the declaration: <snip>
Yes.
C does have "strong typing" (in the sense of Pascal-like name equivalence); it
just doesn't have strong typedef names. C's "strong typing" is exhibited by
enum and struct tags, within a single translation unit.
Not really.
These are distinct, incompatible types even though they have the same
structure:
struct student { int id; };
struct employee { int id; };
In C, struct types are considered for compatibility based on their tag, not
their structure.
Note that tags are not names introduced by a declaration; they are in the tag
namespace. The declaration
typedef struct student { int id;} student;
introduces the tag "student" into the tag namespace and "student" into the
ordinary namespace as a typedef name. (Note that in this regard C++ works a
little bit differently, while preserving compatibility with C to a high
degree.)
Tags in C are in a separate namespace, but both tag names and typedef
names, and other ordinary names, are introduced by declarations.
(Except implicit-old-int functions before C99. And yes C++ differs.)
But within a t.u. only the same *declaration* is the same type,
regardless of using the same tag (which is optional anyway).
struct student { int id; } a;
struct student { int id; } b; /* again in same scope is an error */
int foo (){
struct student { int id; } c; /* is a *different* type */
}
One annoying problem caused by this is:
void func (struct foo * p) ;
/* struct is declared only in scope of the prototype */
struct foo { int id; } bar;
/* intended to be the argument to myfunc but: */
int main (void){ func(&bar); return 0; } /* not compatible call */
void func (struct foo * p){ } /* not compatible definition */
You must either put the struct foo {...} before the function
declaration using it, or put a forward declaration:
struct foo; /* declares the tag but not yet contents */
Anyway, a struct student cannot be assigned to an employee; a pointer to a
struct student cannot be passed to a function that expects a pointer to struct
employee and so forth. They are distinct types because of their distinct tags.
and if you use typedefs like
typedef struct { int id; } Tstudent;
typedef struct { int id; } Temployee;
they are also distinct without using any tags. But:
typedef struct { int id; } Tstudent, Tjunior;
are two typedef names for the same type. So are:
typedef struct { int id; } Tstudent;
typedef Tstudent Tjunior;
This rule is relaxed between translation units, however. In C the model
of multiple compilation is that a translation unit has no access to any
declaration information in another translation unit. Rather, the programmer
must declare what is in another translation unit by providing declarations
(usually deposited in a "header" file).
In the case of a struct and union types, a translation unit can be compiled
using one tag name, like "struct foo", and the declarations for that
translation unit can use another name like "struct bar". This is compatible.
Not officially. n1569 6.2.7p1:
If one is declared with a tag, the
other shall be declared with the same tag. If both are completed
anywhere within their
respective translation units, then the following additional
requirements apply: there shall
be a one-to-one correspondence between their members such that each
pair of
corresponding members are declared with compatible types; if one
member of the pair is
declared with an alignment specifier, the other is declared with an
equivalent alignment
specifier; and if one member of the pair is declared with a name, the
other is declared
with the same name. For two structures, corresponding members shall be
declared in the
same order. For two structures or unions, corresponding bit-fields
shall have the same
widths.
In practice compilers don't vary struct layout for either tag OR
member names, so variations in those 'accidentally' work.
Further often several types use the same representation and work;
e.g. on many (IL32LL64) systems
struct x { int a; };
struct y { long b; };
in different t.u.s will actually work -- but are not guaranteed.
What is going on here is structural compatibility between a declared type, and
a compiled entity in a foreign translation unit about which no type information
is available any more.
In practice yes.