typedef does one small thing: it gives a type that already exists another name. No new type is created, no memory is set aside, and nothing about how the value behaves changes. It is a naming tool - and in C, where the built-in type names run long (unsigned long long, struct Employee, void (*)(int)), a good name is worth a lot.
The Basic Form
The syntax reads like a variable declaration with typedef bolted on the front. Where the variable name would go, you put the new type name:
unsigned long count; // declares a variable named count
typedef unsigned long ulong; // declares a TYPE named ulong
That is the whole rule, and it explains every typedef you will ever read, including the strange-looking ones later on this page.
Note what the Celsius/Fahrenheit pair does not buy you: type safety. Both are just double, so passing a Fahrenheit value where Celsius is expected compiles happily. The names document intent for humans; they do not create a check.
The typedef struct Idiom
This is the reason most C programs contain a typedef at all. In C the type name of a struct includes the keyword:
struct Point { int x; int y; };
struct Point p; /* the word "struct" every single time */
A typedef collapses that:
Read it with the rule from before: strip the typedef and you have struct Point {...} Point;, a declaration of a variable named Point. Add typedef back and Point becomes a type name instead.
With a Tag or Without
You can leave the tag out entirely, giving an anonymous struct with only the typedef name:
typedef struct {
int x;
int y;
} Point; /* no "struct Point" exists - only "Point" */
That is tidier, and for a plain data record it is the common style. But it fails the moment the struct needs to mention itself, because the typedef name is not usable until its own declaration finishes:
/* Does NOT compile: "Node" is not a type yet inside its own braces. */
typedef struct {
int value;
Node *next;
} Node;
Keep the tag and the self-reference works, because struct Node is usable as soon as the tag is seen:
The practical rule: keep the tag. It costs one word, it matches the typedef name so nothing is confusing, and it leaves the door open for self-reference and for forward declarations in headers. See structs and pointers for what that linked node grows into.
Forward Declarations and Opaque Types
Because a pointer to a struct has a known size even when the struct's contents are unknown, a header can hand out a type without revealing its members:
/* stack.h */
typedef struct Stack Stack; /* declared, not defined */
Stack *stack_create(void);
void stack_push(Stack *s, int value);
int stack_pop(Stack *s);
void stack_destroy(Stack *s);
The full struct Stack { ... }; lives in stack.c and nobody outside can touch its members. This opaque type pattern is how C libraries enforce encapsulation, and it is exactly what FILE is in the standard library - you get a FILE * from fopen and are told nothing about what is inside it. See file handling.
typedef for enums and unions
The same idiom applies to the other two composite kinds:
Without the typedef these would be enum Color c; and union Number n;. See enums and unions for what they actually do.
typedef for a Function Pointer
Here is where typedef stops being a convenience and becomes close to necessary. A function pointer's raw syntax is famously hard to read:
void (*handler)(int, const char *); /* a variable */
void register_cb(void (*cb)(int, const char *)); /* a parameter */
Name the type once and both become readable:
Apply the strip-the-typedef rule again: int (*IntBinaryOp)(int, int); alone would declare a variable called IntBinaryOp that points to a function taking two ints and returning an int. With typedef, that becomes the type's name. More in function pointers.
typedef vs #define
They look interchangeable for simple aliases and are not:
#define INT_PTR int *
typedef int *int_ptr;
INT_PTR a, b; /* expands to: int *a, b; -- b is a plain int! */
int_ptr c, d; /* both c and d are int * */
#define is blind text substitution performed before the compiler sees the code; typedef is a real declaration the compiler understands, and it applies to every name in the list. Use typedef for types and reserve #define for constants and macros.
When a typedef Hurts
Typedefs can also hide information the reader needs. The sharpest example is hiding a pointer:
typedef struct Node *NodeRef; /* the star disappears */
NodeRef n = get_node();
Looking at NodeRef n, a reader cannot tell whether n can be NULL, whether it must be freed, whether n and a copy of it share the same object, or whether members are reached with . or ->. All four questions are answered instantly by struct Node *n.
The standard library agrees: it typedefs the struct (FILE) and leaves the star at the point of use (FILE *fp). Two reasonable guidelines fall out:
- Do typedef structs, unions, enums, and function pointers - the noise removed carries no information.
- Do not typedef a pointer just to shorten it, and do not typedef a basic type to disguise it (
typedef int Boolean;invites someone to store7in it - useboolfrom<stdbool.h>instead, covered in booleans).
The standard library's own fixed-width names in <stdint.h> (uint32_t, int64_t, size_t) are all typedefs, and they are the best argument for the feature: they say exactly what they are, they are portable, and nothing is hidden.
Frequently Asked Questions
What does typedef do in C?
It gives an existing type a second name. typedef unsigned long ulong; means ulong and unsigned long are the same type from then on. It creates no new type and no new storage - only a shorter or more descriptive way to spell one you already have.
What is the typedef struct idiom in C?
typedef struct Point { int x; int y; } Point; declares the struct and names the type Point in one statement, so you can write Point p; instead of struct Point p;. Keeping the tag (struct Point) matters when the struct needs to refer to itself, as a linked-list node does.
What is the difference between typedef and #define?
typedef is handled by the compiler and creates a real type alias; #define is a text substitution done by the preprocessor before compiling. That difference bites with pointers: #define PTR int* makes PTR a, b; expand to int* a, b; - only a is a pointer. typedef int *PTR; makes both pointers.
Should I typedef a pointer type in C?
Usually not. typedef struct Node *NodeRef; hides the fact that the type is a pointer, so readers cannot tell whether a variable can be NULL, must be freed, or needs ->. The standard library's FILE * keeps the star visible for exactly this reason. Typedef the struct, not the pointer to it.