typedef 和 #define 起别名的区别
✅ 总结一句话:
🔸
#define
是 预处理器指令,文本替换
🔸typedef
是 编译器关键字,类型定义
它们都可以“起别名”,但本质、作用范围、类型安全完全不同。
🔍 详细对比:typedef
vs #define
对比点 | #define | typedef |
---|---|---|
类型? | ❌ 不是类型(只是文本宏) | ✅ 真正的类型别名 |
本质? | 编译前的 字符替换(预处理器) | 编译阶段的 类型定义 |
可调试性 | 不可调试(调试器看不到) | 可调试(调试器识别为类型) |
作用范围 | 全局宏定义,作用到整个文件 | 遵守作用域规则(如 namespace) |
常用于 | 简写代码片段(如宏、常量) | 简化复杂类型、提升可读性 |
是否安全 | ❌ 无类型检查(可能引起错误) | ✅ 类型安全(检查严格) |
🔸 1. 示例对比:定义 unsigned int
#define uint unsigned int // 宏定义
typedef unsigned int uint_t; // 类型定义
使用:
uint a = 10; // 被替换为 unsigned int a = 10;
uint_t b = 20; // 真正的类型:unsigned int
功能类似,但宏是纯文本替换,编译器不知道 uint
是类型。
#include <stdio.h>
typedef unsigned int uint_t; // 定义 uint_t 为 unsigned int 的别名
int main() {
uint_t b = 20; // 使用 uint_t 定义变量 b
unsigned int c = 30; // 直接使用 unsigned int 定义变量 c
printf("b: %u\n", b); // 输出 b 的值
printf("c: %u\n", c); // 输出 c 的值
return 0;
}
PS E:\ALearnNotes\NewVersionNote\algo\trash> .\define_vs_typedef.exe
b: 20
c: 30
🔸 2. 宏替换的坑(重点)
#define PTR int*
PTR a, b;
你以为是:
int* a;
int* b;
但其实被替换成:
int* a, b; // ❗ 只有 a 是指针,b 是 int!
对比:
typedef int* PTR;
PTR a, b; // ✅ a 和 b 都是 int* 类型
#include <stdio.h>
#define PTR int*
int main() {
PTR a, b; // 实际变成:int* a, b;
int x = 10, y = 20;
a = &x;
b = &y; // ⚠️ 错误:b 实际是 int,不是指针!
printf("*a = %d\n", *a);
// printf("*b = %d\n", *b); // ❌ 会崩溃或报错!
return 0;
}
E:\ALearnNotes\NewVersionNote\algo\trash\define_vs_typedef.cpp: In function 'int main()':
E:\ALearnNotes\NewVersionNote\algo\trash\define_vs_typedef.cpp:9:9: error: invalid conversion from 'int*' to 'int' [-fpermissive]
9 | b = &y; // ⚠️ 错误:b 实际是 int,不是指针!
| ^~
| |
| int*
Build finished with error(s).
#include <stdio.h>
typedef int* PTR_T;
int main() {
PTR_T a, b; // 正确:a 和 b 都是 int*
int x = 10, y = 20;
a = &x;
b = &y;
printf("*a = %d\n", *a);
printf("*b = %d\n", *b);
return 0;
}
PS E:\ALearnNotes\NewVersionNote\algo> & 'c:\Users\31919\.vscode\extensions\ms-vscode.cpptools-1.25.0-win32-x64\debugAdapters\bin\WindowsDebugLauncher.exe' '--stdin=Microsoft-MIEngine-In-kgjqqzl3.ha3' '--stdout=Microsoft-MIEngine-Out-zd0xxvvp.psg' '--stderr=Microsoft-MIEngine-Error-dddj53k4.5d1' '--pid=Microsoft-MIEngine-Pid-nednqhah.rek' '--dbgExe=D:\myapp\MinGW64\bin\gdb.exe' '--interpreter=mi'
*a = 10
*b = 20
📌 typedef
能正确保留类型结构,#define
会误伤。
✅ 所以结论是:
想法 | 推荐 |
---|---|
想简化类型名? | ✅ 用 typedef |
想定义常量/宏? | ✅ 用 #define (或 const 更安全) |
想定义指针别名? | ❌ 不要用 #define ,要用 typedef |
想写类型安全、可调试的代码? | ✅ 用 typedef |
💡 bonus:高级 typedef 用法示例
typedef void (*Callback)(int); // 定义一个函数指针类型
void run(Callback cb) {
cb(42);
}
这句代码:
typedef void (*Callback)(int);
✅ 逐步解析
typedef void (*Callback)(int);
片段 | 含义 |
---|---|
typedef | 定义一个新的类型别名 |
void | 表示函数的返回类型是 void (即没有返回值) |
(*Callback) | 定义了一个名字叫 Callback 的函数指针类型(注意有括号!) |
(int) | 表示这个函数指针指向的函数,参数是一个 int 类型 |
🧠 总体意思
我们定义了一个类型 Callback
,它是一个指向函数的指针类型,而这个函数的特征是:
- 参数:一个
int
- 返回值:
void
(无返回)
📌 等效拆解写法
这句可以等价于下面的结构,但后者可读性差一些:
void func(int); // 普通函数声明
void (*cb)(int); // 定义一个函数指针变量
typedef void (*Callback)(int); // 给这种函数指针类型起了一个别名叫 Callback
现在你就可以这么用了:
void myHandler(int code) {
std::cout << "Callback called with code: " << code << std::endl;
}
int main() {
Callback cb = myHandler; // 把函数赋给函数指针
cb(42); // 调用
return 0;
}
输出:
Callback called with code: 42
🔁 为啥要 typedef 函数指针?
如果不 typedef,每次用函数指针都要写得很麻烦:
void (*cb)(int);
用了 typedef 之后:
Callback cb;
干净又整洁,尤其在回调函数、事件系统、接口封装中非常常用!
✅ 示例 1:多个回调函数 + 动态触发(模拟按钮绑定)
#include <stdio.h>
typedef void (*Callback)(void); // 不带参数的回调函数指针类型
void onClick() {
printf("按钮被点击!\n");
}
void onHover() {
printf("鼠标悬停...\n");
}
void trigger(Callback cb) {
// 动态触发传入的回调
cb();
}
int main() {
trigger(onClick);
trigger(onHover);
return 0;
}
📌 输出:
按钮被点击!
鼠标悬停...
✅ 示例 2:函数指针数组 + 菜单选择(策略模式)
#include <stdio.h>
typedef void (*Operation)(int, int);
void add(int a, int b) { printf("Sum: %d\n", a + b); }
void sub(int a, int b) { printf("Diff: %d\n", a - b); }
void mul(int a, int b) { printf("Prod: %d\n", a * b); }
int main() {
Operation ops[3] = {add, sub, mul};
int choice = 0;
printf("选择操作(0加,1减,2乘):");
scanf("%d", &choice);
if (choice >= 0 && choice < 3) {
ops[choice](10, 5); // 执行对应操作
} else {
printf("无效操作\n");
}
return 0;
}
📌 输入 1
输出:
Diff: 5
✅ 示例 3:结构体中嵌入函数指针(模拟面向对象行为)
#include <stdio.h>
typedef struct {
int x, y;
void (*print)(int, int); // 函数指针作为“方法”
} Point;
void printPoint(int x, int y) {
printf("Point(%d, %d)\n", x, y);
}
int main() {
Point p = {3, 4, printPoint};
p.print(p.x, p.y); // 相当于“调用成员方法”
return 0;
}
✅ 模拟了 p.print()
这种面向对象的行为。
✅ 示例 4:定时器风格的定制回调系统(自定义逻辑)
#include <stdio.h>
#include <time.h>
typedef void (*Task)(void);
void sayHi() {
printf("Hi!\n");
}
void delayExec(int seconds, Task task) {
printf("等待 %d 秒后执行...\n", seconds);
time_t start = time(NULL);
while (time(NULL) - start < seconds); // 简单阻塞等待
task();
}
int main() {
delayExec(2, sayHi);
return 0;
}
📌 模拟了“定时器+回调执行”,等 2 秒后自动调用函数。
🔚 总结应用场景
类型 | 示例 |
---|---|
动态执行行为 | trigger(cb) |
策略选择 | 函数指针数组 |
结构体+指针 | 模拟类成员方法 |
定时回调 | 延时函数调用 |
异步框架 | 网络事件/中断处理回调 |