🔷 第八章 结构体与共用体
📄 一句话概括

typedef 的作用就一句话:给一个已经存在的类型起个新名字。格式是 typedef 原类型 新名字;——注意新名字写在最后,而且没有变量、没有 =。它最常见的两个用途:把长得吓人的类型名变短(unsigned long long → uint64),把结构体名变成"像内置类型一样好用"(Student s; 而不是 struct Student s;)。它只是换名字,不产生新类型。

📝 本节和前面几节的关系

8.1 里我们一直写 Student s1;——其实在纯粹的老 C 里必须写 struct Student s1;,那个 struct 关键字很累赘。本节就是解决这类"名字太啰嗦"的痛点。你会看到它和我们 2.2.5 学过的 #define 很像,但两者有本质区别(最后会给一张对照表,这是本节最容易考也最容易错的地方)。

一、基本用法:typedef 原类型 新名字;

typedef unsigned long long  uint64;      // 给长名字起短名
typedef int                 Score;       // 给 int 起一个业务名

读法:typedef 后面先是原类型,最后是新名字,中间用空格隔开,末尾分号。

用起来就跟原来的类型完全一样:

typedef unsigned int uint;

uint a = 10;            // 等同于 unsigned int a = 10;
uint b, c;              // 一次声明多个也行

一句话记住顺序:typedef 旧的 新的;——新的在最后。很多人写成 typedef uint unsigned long long; 就错了(编译器会当成"给 uint 起名叫 unsigned")。

二、给结构体起名:本章最关心的一种

typedef struct Student {
    char   name[20];
    int    age;
    double score;
} Student;                      // ← 新名字写在最后

这样之后就可以省掉 struct 关键字,写起来和内置类型一模一样:

Student s1;                     // 不用再写 struct Student s1;

说明: - 在 C 语言里这几乎是必需的,因为老 C 不给结构体自动生成类型名,每次都得写 struct Student; - 在 C++ 里 struct Student {...}; 之后就能直接用 Student 了(这就是我们从 8.1 开头一直这么写的原因)——所以 C++ 里给别人写好的结构体加 typedef,主要是为了兼容 C 代码或统一风格; - 上面这种"定义 + 别名"一把做完的写法,在教材里非常常见,看到要知道它等价于"定义结构体 + typedef 起名"两件事。

三、还能给数组、指针起名字

typedef 的"原类型"位置可以放任何类型描述,包括数组和指针:

typedef int  Arr10[10];         // Arr10 是"10 个 int"这种类型
typedef int *IntPtr;            // IntPtr 是"指向 int 的指针"这种类型

Arr10  a;                       // 等于 int a[10];
IntPtr p, q;                    // p、q 都是 int*(关键,见下节对比)

typedef int Arr10[10]; 读法:把 Arr10 替换进原文,就得到 int a[10];——新名字就是"变量名位置"上的那个词。

四、和 #define 的区别(重点,容易考)

两者都能"换个名字",但本质完全不同:

typedef char *PChar;
PChar a, b;                     // ✅ a 和 b 都是 char*

#define PCHAR char *
PCHAR c, d;                     // ❌ c 是 char*,但 d 只是 char!

为什么?

typedef #define
谁处理 编译器(语言的一部分) 预处理器(编译前的文字替换)
替换方式 建立"类型别名" 纯文本替换,直接贴进去
上面那个例子 PChar a, b; → a、b 都是 char* PCHAR c, d; → 变成 char *c, d; → d 只是 char
有类型检查吗 有 没有,它根本不知道类型
作用域 有(可以写在函数里) 无(从定义处到文件末尾全局生效)
末尾分号 必须有 不能有(加了就把分号一起替换进去)

结论:只要能用 typedef,就别用 #define 去定义类型别名。 这是 2.2.5 那条"const 优先于 #define"的同一道理——让编译器认识它,比让预处理器瞎贴强。

五、C++ 的更现代写法:using

C++11 起还有一种更好读的别名写法:

using uint64 = unsigned long long;      // 新名字在前,等号在中间
using Student = struct Student;         // 也可以给结构体起名

和 typedef 的差别:using 新名字写在前面、用 =,读起来更顺("uint64 就是 unsigned long long")。

教材环境(老 MinGW)可能不支持 using 别名——学习阶段先掌握 typedef,见到 using 知道是同一回事就行。

六、动手 Demo:三种别名一起用

#include <iostream>
#include <cstring>
using namespace std;

// ① 给长类型起短名
typedef unsigned long long uint64;
typedef unsigned char      byte;

// ② 给结构体起名(教材最常见写法)
typedef struct Student {
    char   name[20];
    int    age;
    double score;
} Student;

// ③ 给数组类型起名
typedef int Arr5[5];

int main() {
    uint64 big = 1234567890123ULL;      // 比 unsigned long long 短得多
    byte   b   = 200;

    Student s;                          // 不用写 struct Student
    strcpy(s.name, "张三");
    s.age = 18;  s.score = 92.5;
    cout << s.name << " " << s.age << " " << s.score << endl;

    Arr5 nums = {1, 2, 3, 4, 5};        // 等于 int nums[5];
    for (int i = 0; i < 5; i++) cout << nums[i] << " ";
    cout << endl;

    cout << "big = " << big << ", b = " << (int)b << endl;
    cout << "sizeof(uint64) = " << sizeof(uint64) << endl;
    return 0;
}

看到什么算成功:张三 18 92.5、1 2 3 4 5、big = 1234567890123, b = 200、sizeof(uint64) = 8。

30 秒实验:把 ③ 的 typedef int Arr5[5]; 删掉,改成直接写 int nums[5] = {...};——输出一模一样。这证明 typedef 不改变任何行为,只是让名字好写。

七、常见误区

  1. "typedef 会创建新类型"——不会。它只是别名,uint 和 unsigned int 是完全相同的类型,可以互相赋值、不会触发类型检查报错。(真正"造新类型"的是 enum、class。)
  2. "新名字写在前面"——写在最后:typedef 原类型 新名字;。写反了语义就变了,而且往往不报错、只是"起了个奇怪的名字"。
  3. "typedef 和 #define 差不多"——差得很远:一个在编译期由编译器处理、有类型有作用域;一个在预处理期做纯文本替换。指针别名那个例子就是照妖镜。
  4. "typedef 后面可以跟 ="——不行,那是 using 的写法(using A = B;)。
  5. "给结构体起了别名,原来的名字就不能用了"——两个名字并存,struct Student 和 Student 都能用。
自测三问(点开对答案) 1. 写一句"把 `unsigned int` 起名叫 `uint`"。——`typedef unsigned int uint;`(新名字在最后) 2. `typedef char *PChar; PChar a, b;` 里 `b` 是什么类型?用 `#define` 写会怎样?——`b` 是 `char*`。改用 `#define PCHAR char *` 后,`PCHAR c, d;` 会展开成 `char *c, d;`,**`d` 只是 char**。 3. `typedef` 和 `#define` 最本质的区别是什么?——`typedef` 由**编译器**处理,是真正的**类型别名**(有类型、有作用域);`#define` 由**预处理器**做**纯文本替换**(无类型、无作用域)。

相关笔记