C 语言 typedef
更新时间:2026-08-26。本文是
languages/c/主题入门层第 47 篇。写代码时struct Student *p这种写法又长又啰嗦,unsigned long long更是手指地狱。C 提供了给类型起别名的工具——typedef,代码瞬间清爽,还能为平台差异留出改一处即生效的"开关"。
本文要回答的问题
typedef怎么写?能起哪些别名?- 给结构体、指针、函数指针起别名各有什么讲究?
- 它和
#define宏有什么区别? - 用
typedef给指针起别名时,const到底修饰谁?
一、基本语法
typedef 原类型名 新名字;typedef unsigned long long u64;
typedef float Score; // 语义化命名
u64 n = 18446744073709551615ULL; // 之后直接用新名字
Score s = 95.5;读法:把 typedef 去掉,剩下的声明语句是"定义了一个名为新名字的变量",新名字就是类型别名。比如 typedef unsigned long long u64; 去掉 typedef 后是 unsigned long long u64;——一个类型为 unsigned long long 的变量 u64,所以 u64 就是别名。
二、给结构体起别名
这是最常用的场景,省掉每次的 struct 关键字:
typedef struct {
int id;
char name[32];
} Student; // 匿名结构体 + 别名
Student s = {1001, "alice"}; // 不用写 struct Student
Student *p = &s;对比一下原写法:
| 写法 | 声明 | 备注 |
|---|---|---|
| 原版 | struct Student s; | 每次都带 struct |
| typedef 版 | Student s; | 别名即类型,干净 |
带标签的写法也常见,二者等价:
typedef struct Student {
int id;
char name[32];
} Student;三、给指针起别名:const 陷阱
typedef char *String; // char* 的别名问题来了:const String s; 到底等价于 const char *s 还是 char *const s?
答案是后者——const 修饰的是别名代表的整个类型(即指针本身),而不是指针指向的东西:
typedef char *String;
const String s; // 等价 char *const s:指针本身不可改
const char *t; // 指向的字符不可改陷阱:初学者想写"指向 const char 的指针",却写 const String,结果完全相反。这也是很多工程规范(如 Linux 内核风格)不提倡给指针类型用 typedef 的原因之一——可读性反而下降。一般建议:别名给"值类型"(结构体、整型)最安全;给指针起别名要谨慎,尤其是要加 const 时。
四、给函数指针起别名
函数指针的声明绕口,typedef 是它的救星:
// 不 typedef 的写法:每次声明都很痛苦
int (*cmp)(const void *, const void *);
// typedef 后:类型像一个普通类型
typedef int (*CompareFn)(const void *, const void *);
CompareFn f = compare_int; // 声明像声明变量回调、策略表、事件分发场景里,函数指针别名让代码可读性大幅提升,也是后续高手层函数指针高级用法的地基。
五、typedef vs #define
二者都能"造名字",但机制完全不同:
#define PTR char*
PTR a, b; // 展开成 char* a, b; → b 是 char,不是指针!
typedef char *ptr_t;
ptr_t a, b; // a、b 都是 char*| 对比项 | #define | typedef |
|---|---|---|
| 本质 | 预处理文本替换 | 编译期类型别名 |
| 是否支持逗号列表 | 支持(但语义会错) | 支持(语义正确) |
| 是否受作用域限制 | 否,全局可见 | 受作用域限制 |
| 能否被 sizeof 使用 | 不能 | 能 |
| 可读性/类型安全 | 差 | 好 |
结论:起类型别名一律用 typedef,#define 留给"常量/宏函数"。
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
const String 语义反了 | 该只读的数据被改 | 指针别名慎用 const,或不用指针别名 |
| 用 #define 起别名 | 逗号声明出错、无类型检查 | 用 typedef |
| 别名与变量混淆 | 可读性差 | 别名用驼峰/语义名,变量用小写 |
| 在函数里 typedef | 作用域太小 | 全局/头文件里定义 |
| 忘掉"去掉 typedef 读声明" | 理解不了复杂声明 | 逐层拆解 |
七、与本站主线衔接
- typedef + 匿名结构体常与联合体 union、枚举 enum 搭配,构成 C 的"自定义类型三件套";
- 函数指针别名的进阶用法(回调注册、策略表),见函数指针高级用法;
- typedef 也常用于屏蔽平台差异(如
size_t本身就是一个 typedef),与整型大小与平台相关。
一句话总结
typedef 给类型起别名(去掉 typedef 后声明一个变量,变量名就是别名),结构体/函数指针最常用;它编译期生效、类型安全,完胜 #define 的文本替换;但给指针起别名时 const 会修饰指针本身而非指向对象,容易踩坑——别名是"给类型起外号",不是"给宏换皮"。