typedef 和 #define 起别名的区别

typedef 和 #define 起别名的区别

✅ 总结一句话:

🔸 #define预处理器指令文本替换
🔸 typedef编译器关键字类型定义

它们都可以“起别名”,但本质、作用范围、类型安全完全不同。


🔍 详细对比:typedef vs #define

对比点#definetypedef
类型?❌ 不是类型(只是文本宏)✅ 真正的类型别名
本质?编译前的 字符替换(预处理器)编译阶段的 类型定义
可调试性不可调试(调试器看不到)可调试(调试器识别为类型)
作用范围全局宏定义,作用到整个文件遵守作用域规则(如 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)
策略选择函数指针数组
结构体+指针模拟类成员方法
定时回调延时函数调用
异步框架网络事件/中断处理回调

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值