typedef 是 C 语言(以及 C++)中的一个关键字,它的作用是为已有的数据类型创建一个新的名称(别名),你可以把它理解为一种“类型定义器”或“类型重命名器”。

(图片来源网络,侵删)
typedef 的基本语法
typedef 的基本语法格式如下:
typedef existing_type new_type_name;
existing_type: 已经存在的数据类型,可以是基本数据类型(如int,char)、构造数据类型(如struct,union)、指针类型,甚至是函数指针类型。new_type_name: 你为这个类型创建的新名字,这个新名字在后续的代码中可以直接用来声明变量。
typedef 的主要用途和示例
为基本数据类型创建别名
这是最简单的用法,通常用于提高代码的可读性或简化代码。
场景:在程序中,我们经常需要处理表示“金额”或“年龄”的变量,直接使用 int 可能不够清晰。
#include <stdio.h>
// 使用 typedef 为 int 创建一个别名叫 Money
typedef int Money;
// 为 unsigned int 创建一个别名叫 Age
typedef unsigned int Age;
int main() {
// 现在可以直接使用 Money 和 Age 来声明变量
Money my_salary = 10000;
Age my_age = 30;
printf("My salary is: %d\n", my_salary); // 输出: My salary is: 10000
printf("My age is: %u\n", my_age); // 输出: My age is: 30
// my_salary 和 my_age 本质上仍然是 int 类型
printf("Size of my_salary: %zu bytes\n", sizeof(my_salary)); // 输出: 4 bytes
return 0;
}
优点:

(图片来源网络,侵删)
- 可读性增强:
Money salary比int salary更能表达变量的含义。 - 代码维护性:如果将来需要将
Money类型从int改为long long,只需修改typedef这一行即可,而不需要在整个代码库中搜索所有int并判断是否与金额相关。
为结构体(struct)创建别名
这是 typedef 最常用、最重要的场景之一,在 C 语言中,定义结构体变量时通常需要使用 struct 关键字,这会显得很冗长。
没有 typedef 的写法(传统C风格):
struct Point {
int x;
int y;
};
int main() {
// 声明结构体变量时必须带上 struct 关键字
struct Point p1;
p1.x = 10;
p1.y = 20;
return 0;
}
使用 typedef 的写法(现代C风格):
#include <stdio.h>
// 定义结构体 Point,并立即为其创建一个别名叫 POINT
typedef struct {
int x;
int y;
} POINT;
// 也可以分开写,效果相同
// struct Point {
// int x;
// int y;
// };
// typedef struct Point POINT;
int main() {
// 现在可以直接使用 POINT 来声明变量,无需 struct 关键字
POINT p1;
p1.x = 10;
p1.y = 20;
// 也可以同时声明多个变量
POINT p2 = {30, 40};
printf("p1: (%d, %d)\n", p1.x, p1.y); // 输出: p1: (10, 20)
printf("p2: (%d, %d)\n", p2.x, p2.y); // 输出: p2: (30, 40)
return 0;
}
注意:在 C++ 中,struct 的定义方式有所不同,可以不带 struct 关键字直接声明变量,typedef 在这里的作用不像在 C 中那么巨大,但它仍然是一种良好的编程习惯。

(图片来源网络,侵删)
为指针类型创建别名
typedef 也可以为指针创建别名,这在处理复杂数据结构(如链表、树)时非常有用。
场景:定义一个指向 Node 结构体的指针。
#include <stdio.h>
#include <stdlib.h>
// 定义一个链表节点结构体
typedef struct Node {
int data;
struct Node* next; // 在结构体内部,必须使用完整的 struct Node
} Node;
// 使用 typedef 为指针创建别名
typedef Node* NodePtr; // NodePtr 是一个指向 Node 结构体的指针类型
int main() {
// 使用 Node 声明结构体变量
Node node1;
node1.data = 10;
// 使用 NodePtr 声明指针变量,比写 struct Node* 更简洁
NodePtr ptr_to_node1 = &node1;
ptr_to_node1->data = 20; // 使用箭头操作符访问成员
printf("Node data via pointer: %d\n", ptr_to_node1->data); // 输出: Node data via pointer: 20
return 0;
}
为数组类型创建别名
虽然不常用,但 typedef 也可以为数组创建别名。
#include <stdio.h>
// 定义一个包含 10 个整数的数组类型
typedef int INT_ARRAY_10[10];
int main() {
// 使用 INT_ARRAY_10 声明一个数组
INT_ARRAY_10 my_array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
// 也可以声明一个指向这种数组的指针
INT_ARRAY_10 *ptr_to_array = &my_array;
printf("First element: %d\n", my_array[0]); // 输出: First element: 0
printf("First element via ptr: %d\n", (*ptr_to_array)[0]); // 输出: First element via ptr: 0
return 0;
}
为函数指针创建别名
这是 typedef 最复杂但也最强大的功能之一,尤其在回调函数、事件驱动编程中非常关键。
函数指针的原始写法:
假设有一个函数 int add(int a, int b),指向它的指针声明为:
int (*func_ptr)(int, int); 这种写法非常晦涩难懂。
使用 typedef 简化:
#include <stdio.h>
// 一个普通的加法函数
int add(int a, int b) {
return a + b;
}
// 1. 定义函数指针类型
// 语法可以理解为:typedef 返回类型 (*新类型名)(参数列表);
typedef int (*CalcFuncPtr)(int, int);
// 2. 使用这个新类型名来声明函数指针变量
CalcFuncPtr func_ptr;
int main() {
// 3. 将函数名赋给函数指针变量
func_ptr = add;
// 4. 通过函数指针调用函数
int result = func_ptr(5, 3);
printf("Result: %d\n", result); // 输出: Result: 8
// 也可以直接在声明时初始化
CalcFuncPtr subtract_ptr = (CalcFuncPtr)/*假设有一个subtract函数*/ NULL;
// ...
return 0;
}
更复杂的例子:回调函数
#include <stdio.h>
// 定义一个函数指针类型,用于回调
typedef void (*CallbackFunc)(int);
// 一个执行某些操作并调用回调函数的函数
void process_data(int data, CallbackFunc callback) {
printf("Processing data: %d\n", data);
// 假设处理完成
callback(data);
}
// 回调函数的具体实现
void on_complete(int result) {
printf("Callback executed with result: %d\n", result);
}
int main() {
// 将 on_complete 函数作为参数传递
process_data(100, on_complete);
return 0;
}
typedef vs. #define
初学者很容易将 typedef 和 #define 混淆,因为它们都可以用来创建别名,但它们有本质的区别:
| 特性 | typedef |
#define |
|---|---|---|
| 本质 | 关键字,编译器处理 | 预处理器指令,在编译前进行文本替换 |
| 作用域 | 遵循C语言的作用域规则(如 内有效) | 全局有效,没有作用域概念 |
| 处理方式 | 在编译时处理,是一种真正的类型定义 | 在预处理时处理,只是简单的“复制粘贴” |
| 与类型 | 与类型绑定,用于定义类型别名 | 与类型无关,只是宏替换,可以替换任何文本 |
| 示例 | typedef int MyInt; |
define MyInt int |
| 安全性 | 更安全。typedef char* STRING1, STRING2; 定义了两个指针,而 #define STRING1 char*; STRING2 char; 会导致 STRING2 被错误地定义为 char。 |
不安全,容易因替换规则导致意想不到的错误。 |
typedef的核心作用是为类型创建别名,使代码更清晰、简洁、易于维护。- 它可以用于基本类型、结构体、指针、数组、函数指针等几乎所有数据类型。
- 对于结构体,
typedef能极大地简化代码,避免重复书写struct关键字。 - 对于函数指针,
typedef能将复杂的声明变得清晰易读,是实现回调等高级特性的基础。 - 它与
#define有本质区别,typedef是编译器行为,而#define是预处理器行为,typedef更安全、更强大。
掌握 typedef 是从 C 语言新手走向熟练的重要一步。
