For syntactic convenience, typedef
is treated as a storage class specifier, like extern
, static
, or register
. Semantically of course it's quite different, but when typedef
was added to the language, it was simpler to use an existing piece of the grammar to define its syntax.
Adding static
to an object declaration doesn't change the meaning of the declaration except that it changes the object's storage class to "static" (if it wasn't already):
{
int foo; /* automatic storage duration */
static int bar; /* static storage duration */
}
Replacing static
with typedef
changes the meaning of the declaration, so that the name being defined is not an object name but a type name (actually just an alias for an existing type):
typedef int baz; /* "baz" is an alias for "int" */
The same syntax applies to more complex declarations:
int (*a)[42]; /* a is a pointer to an array of 42 ints */
static int (*b)[42]; /* Same as a, but with static storage duration */
typedef int (*c)[42] /* c is an alias for the type int(*)[42], or
"pointer to array of 42 ints" */
Once you realize that typedef
was arbitrarily shoved into the same slot in the grammar occupied by extern
, static
, and register
, understanding typedef
declarations is no harder (and no easier!) than understanding object declarations. (The cdecl
program and web site are useful for unpacking complex declarations.)
You can also have a typedef
for a function type:
void func(void); /* declare func as a function */
typedef void func_type(void); /* declare func_type as a name
for a function type */
You can't use a typedef
ed function type to declare or define a function, but you can use it to declare a function pointer:
func_type *ptr = func;
As for scope (meaning the region of program text over which a declared identifier is visible), an identifier defined by a typedef
declaration has the same scope as any other declared identifier. If it's declared at file scope, outside any function, it's visible from the point of declaration to the end of the file. If it's declared inside a function, it's visible from the point at which it's declared to the end of the nearest enclosing block. And like any declaration, it can be hidden by another declaration with the same name in an inner scope.