Visual walkthrough — Typedef
Step 1 — What a plain declaration really says
WHAT. Start with the simplest possible line of C:
int x;WHY start here. typedef copies this exact shape. If we understand how the eye and the compiler read a variable declaration, we get typedef for free — because typedef is literally this line with one word glued on the front.
PICTURE. A declaration in C has two jobs pinned to two locations. On the left sits the base type (int). In the middle sits the name being introduced (x). Read it as a sentence:
int— the kind of thing.x— the label we can use to refer to a thing of that kind.

Step 2 — Glue typedef on the front
WHAT. Put the keyword typedef in front of the same line:
typedef int Integer;WHY. The C designers made a deliberate choice: instead of inventing brand-new syntax for aliases, they reused the declaration syntax you already know. The only rule they added is one flip of meaning, shown next.
PICTURE. Everything looks identical to Step 1, except a word appears on the far left:
typedef— a switch. It tells the compiler: "do not create a variable; instead, whatever name sits in the name slot becomes a new name for a type."int— still the base type.Integer— sits in the name slot. SoIntegeris now an alias forint.

Step 3 — Why the * sticks to the name, not the type
WHAT. Now a line with a pointer:
char *String; // without typedefWHY this matters. This is the single trickiest thing in all of C declarations, and the whole "pointer typedef surprise" flows from it. We must see where the * binds before we can trust any pointer alias.
PICTURE. The * is not part of the base type char. It is glued to the name String. Read the declaration as: "*String is a char", i.e. "when you dereference String, you get a char", i.e. String is a pointer to char.
char— base type (what you get after following the pointer).*— binds to the name, meaning "pointer to...".String— the name.

Step 4 — Add typedef to the pointer line
WHAT.
typedef char *String;WHY. Apply Steps 2 and 3 together. Glue typedef on; the name slot's identifier is String; the type that identifier would have had is "pointer to char". So String becomes an alias for char *.
PICTURE. Same layout as Step 3, with the switch flipped:
Because the * was baked into the alias in Step 3, it is now part of the type String names. This is the seed of the famous surprise in Step 5.

Step 5 — The multi-name surprise (edge case)
WHAT. Declare two names with a pointer alias vs. a raw *:
typedef int* IntPtr;
IntPtr a, b; // a: int* b: int* (BOTH pointers)
int* c, d; // c: int* d: int (only c!)WHY show this. This is the degenerate case where beginners are burned. We must prove why the results differ — and the answer is entirely from Step 3.
PICTURE. In int* c, d; the * binds to the first name only (c); d reuses just the base type int. But IntPtr is one whole type — the * is already inside it — so every name in the list inherits the complete pointer type.
- top line: the arrow shows the
*reaching onlyc. - bottom line: no loose
*exists — the pointerness lives insideIntPtr, so bothaandbare full pointers.

Step 6 — The struct case: dropping a keyword
WHAT.
struct Point { int x, y; };
typedef struct Point Point; // alias 'Point' for 'struct Point'
Point p; // instead of 'struct Point p;'WHY. In C, struct tags live in a separate namespace, so the full type name is two words: struct Point. Apply our rule: remove typedef → struct Point Point; declares a variable named Point of type struct Point. So the alias Point now means struct Point, and we may drop the keyword forever.
PICTURE. Two namespaces sit side by side; the typedef builds a bridge from the plain name Point (type namespace) to the tagged type struct Point.

Step 7 — The boss: a function-pointer typedef
WHAT. Decode the ugliest common form:
typedef int (*Operation)(int, int);WHY. Function-pointer syntax scares everyone. But our rule still works — the name slot is buried in the middle, and finding it is the whole game. See Function Pointers.
PICTURE. Remove typedef, then hunt the name slot. The parentheses (*Operation) group the * with the name (forcing "pointer to function", not "function returning pointer"). Reading outward from the name:
Operation— the name slot ⇒ this becomes the alias.(* ... )— pointer, parenthesised so it binds before the call parens.(int, int)— the parameters of the function.int(far left) — the function's return type.
So Operation = "pointer to a function taking two ints and returning int." Usage:
int add(int a, int b){ return a + b; }
Operation op = add;
int r = op(2, 3); // r = 5
The one-picture summary
WHAT. One flow that compresses every step: cover typedef, read as a variable declaration, the name slot is your alias, whatever type that name would have had is the alias's meaning — and the * always rides with the name.

Recall Feynman: retell the whole walkthrough
A normal C line, like int x;, has two slots: a type on the left and a name somewhere in the middle. Put the magic word typedef in front and you flip one switch: the name in that middle slot is no longer a variable — it becomes a new name for a type, and the type it would have had is what the name now means. The trickiest part is the little star *: it always sticks to the name, not the base type. That's why typedef int* IntPtr; makes IntPtr a, b; give you two pointers, while int* a, b; gives only one. Structs need two words (struct Point), so a typedef bridges to a one-word name. And even the scary function-pointer form obeys the exact same rule: dig out the buried name, and that name is your alias. One trick, every case.
Connections
- Typedef — parent topic.
- Structures in C — Step 6, dropping the
structkeyword. - Pointers in C — Steps 3–5, where the
*binds. - Function Pointers — Step 7, the boss decode.
- Linked Lists — real-world
typedef struct node Node;. - Hinglish version →