Typedef
使用例

まずtypedefを使わない例を示す。
int is_even_number(int x) {
    return x % 2 == 0;
}
この関数は入力xが偶数であれば1を、奇数であれば0を返す。しかし、次のようにint型の別名として論理型（ブーリアン型）を定義し、関数の戻り値の型の名前として使用することで、戻り値の意味を明確化できる。
typedef int my_boolean_t;

my_boolean_t is_even_number(int x) {
    return x % 2 == 0;
}
真偽値 (boolean) は通例0を偽、0以外を真とすることから、関数の仕様がより明確となっている。サフィックス_tは、typedefによる型エイリアスであることを分かりやすくするために付けられることが多い（size_tやtime_tなど）。
また、typedefは長大で複雑な型名を単純化するために用いられることもある。
typedef unsigned char byte;
typedef unsigned long long ulonglong;

byte vb = UCHAR_MAX;
ulonglong vull = ULLONG_MAX;
次に、構造体を使った例を示す。
struct my_struct {
    int data1;
    int data2;
    char data3;
};
ここでは、ユーザー定義の型my_structが定義されている。my_struct型の変数を宣言するために、C言語では以下のようにstructキーワードが必要である。
struct my_struct a;
my_struct b; /* 構文エラー */
ここで、構造体宣言の後に、以下の行を追加してみる。
typedef struct my_struct my_struct_t;
これにより、my_struct型の変数を宣言するためには、以下のコードで十分となる。
my_struct_t a;
同じことは、以下のコードでも行うことができる。
typedef struct my_struct {
    int data1;
    int data2;
    char data3;
} my_struct_t;
構造体のタグ名を省略することもできる。この場合、タグ名は処理系によって自動生成される。
typedef struct {
    int data1;
    int data2;
    char data3;
} my_struct_t;
構造体のタグ名と別名を同一にすることもできる。
typedef struct my_struct_t {
    int data1;
    int data2;
    char data3;
} my_struct_t;
以上のtypedef活用は構造体だけに限らず、共用体unionや列挙型enumの定義時にも同様に適用可能である。
typedefは、自己参照構造体へのポインタの作成を簡単にすることもできる。以下にコード例を示す。
typedef struct node_tag node_t;

struct node_tag {
    node_t *nextptr;
    int data;
};
通常、ある型のポインタ型変数を宣言する際、それぞれの変数名の前にアスタリスク*を記述する必要がある。
node_t *startptr, *endptr, *curptr, *prevptr, errptr, *refptr;
プログラマはerrptrがポインタ型node_t *であることを想定しているが、typoによりerrptrは値型node_tであると定義されてしまっている。これは微妙な構文エラーを引き起こす。
新たにnode_t *型を定義することでこのような記述ミスを回避できる。以下のコードがその例である。
typedef node_t *node_ptr_t;

node_ptr_t startptr, endptr, curptr, prevptr, errptr, refptr;
これにより、errptrを含めて全ての変数がnode_t *型であることが保証される。
再代入や書き換えが不可能な文字列定数の配列（テーブル）を定義するときも、typedefを利用することで分かりやすくなる。
#if 0
static const char *const planetNamesTable[] = { "Mercury", "Venus", "Earth", "Mars" };
#else
/* 以下は上記と等価。 */
typedef const char *ConstCharPtr;
static const ConstCharPtr planetNamesTable[] = { "Mercury", "Venus", "Earth", "Mars" };
#endif
関数へのポインタを利用する場合も typedef を利用することで可読性を向上できる可能性がある。
#include <stdio.h>

/* 関数型のエイリアス */
typedef int binary_operator_t(int, int);

static int add(int a, int b) { return a + b; }

int main(int argc, char *argv[]) {
#if 0
    int(*f)(int, int) = add;
#else
    /* 以下は上記と同義だが、より簡潔かつ明瞭である。 */
    binary_operator_t *f = add;
#endif

    printf("add(12, 13) = %d\n", f(12, 13));
}
なお、上記は以下のようにも書ける。
#include <stdio.h>

/* 関数ポインタ型のエイリアス */
typedef int (*binary_operator_ptr_t)(int, int);

static int add(int a, int b) { return a + b; }

int main(int argc, char *argv[]) {
    binary_operator_ptr_t f = add;

    printf("add(12, 13) = %d\n", f(12, 13));
}
関数型もしくは関数ポインタ型のエイリアスを定義しておくと、特にコールバック関数へのポインタを引数として受け取る関数を定義するときに記述性や可読性が向上する。
配列に対して typedef を利用することもできる。
#include <stdio.h>

typedef int array_int_2_t[2];

int main(int argc, char *argv[]) {
    array_int_2_t a;

    a[0] = 12;
    a[1] = 13;

    printf("a[0] = %d, a[1] = %d\n", a[0], a[1]);
}
なお、以下のようなコードに対する動作は未定義となる。グローバルスコープを持ち、アンダースコア_で始まる識別子、もしくは_で始まり、その次が大文字の識別子は予約済み識別子であり、それを宣言または定義した場合は動作未定義とされているからである。
typedef struct _MyStruct {
    ...
} MyStruct;