C++启蒙课程:第4章  复合类型

编程小英雄们,我们学会了处理单个数字和字符。今天,我们将掌握数组、结构和指针这些强大的“超级数据容器”!

🗄️

连体储物柜:数组

学习如何使用连续编号的储物柜(数组)来批量存储数据。

📦

智能百宝箱:结构

学习如何定制能装下不同类型物品的超级百宝箱(结构)。

🧭

地址魔法:指针

掌握C++最强大的魔法——内存导航仪(指针)和动态内存。

课时一:连体储物柜:数组与C-风格字符串

(覆盖知识点 4.1.1 - 4.2.5)

4.1 连体储物柜 (数组)

数组是连续编号的储物柜,编号从0开始。C++11禁止“缩窄转换”(如把小数塞进整数柜)。

// 声明一个有5个整数的数组 int scores[5]; // 初始化 (C++11) int arr[5] = {1, 2}; // {1, 2, 0, 0, 0} // 错误!2.5 是 double,不能塞进 int 柜子 // int arr[1] = {2.5};

4.2 字符链与空字符幽灵

C-风格字符串是 `char` 数组,末尾藏着一个隐形的“幽灵”——空字符 `\0`,告诉程序“我在这里结束了”。

'H' 'e' 'l' 'l' 'o' 👻(\0)
// "Hello" 包含 5 个字符 // 但数组大小必须是 6,为 \0 留出空间 char name[6] = "Hello"; // 编译器自动在末尾添加 \0 // 内存中是: ['H','e','l','l','o','\0']

4.2.3 `cin` vs `getline`

`cin` 机器人遇到空格就停下。`getline` 机器人会拿走一整行。

char name[50]; // cin 只会读取 "Hello" cin >> name; // cin.getline 会读取 "Hello World" cin.getline(name, 50);

4.2.5 混合输入陷阱

`cin >> age;` 会在输入流中留下一个“回车符垃圾” (\n),导致下一个 `getline` 立刻读到它并停止。

输入流:
5 🗑️(\n)
int age; char name[50]; cin >> age; // 必须在这里“吃掉”垃圾 cin.get(); // <-- 修复陷阱! cin.getline(name, 50);

课时二:智能百宝箱:string类与结构

(覆盖知识点 4.3.1 - 4.6.2)

4.3 智能弹性绳 (string)

`string` 类是更现代的字符串。它像一根弹性绳,可以自动调整大小,并用 + 号轻松拼接。

Hello
#include <string> // 需要头文件 using namespace std; string s1 = "Hello"; string s2 = "World"; // 用 + 号轻松拼接 string s3 = s1 + " " + s2; // s3 is "Hello World"

4.4 定制超级百宝箱 (struct)

结构 (struct) 是一种蓝图,让你能定制一个可以装下 *不同类型* 物品的“百宝箱”。

struct Student
string name
int age
// 定义一个“学生”蓝图 struct Student { string name; int age; }; // 创造一个真实的学生“百宝箱” Student s; // 使用 . 运算符访问成员 s.name = "Alice"; s.age = 10;

4.5 共享内存格 (union)

共用体 (union) 只有一个内存格,它在同一时间 *只能* 存储一种类型的值。

...
union OneValue { int i; double d; }; OneValue v; v.i = 99; // 现在它存储 int // v.d = 3.14; // 现在它存储 double (int 99 丢失)

4.6 身份牌常量 (enum)

枚举 (enum) 让你为整数创建有意义的名字(身份牌),默认从 0 开始。

点击身份牌查看整数值:
...
// Red=0, Blue=1, Green=2 enum Color { Red, Blue, Green }; Color my_color = Blue; if (my_color == Blue) { // ... }

课时三:地址魔法:指针与动态内存

(覆盖知识点 4.7.1 - 4.8.5)

4.7.1 内存导航仪 (指针)

指针 (* pt) 是一个变量,它不存值,只存另一个变量的“地址”。& 用来取地址,* 用来取地址上的值。

val = 5 (地址: 0x1000)
pt = ... (一个指针)
int val = 5; int* pt; // pt 是一个指向 int 的指针 pt = &val; // & 取 val 的地址,存入 pt // * (解除引用) 查看 pt 指向的值 cout << *pt; // 输出 5

4.7.4 自由魔法乐园 (Heap)

new 运算符在“自由存储区”(堆)中申请一块内存。你必须用 delete 手动归还它,否则会“内存泄漏”!

自由魔法乐园 (Heap)
// 申请一块 int 内存 int* pn = new int; *pn = 10; // 使用它 // 必须手动释放! delete pn; // 动态数组 int* arr = new int[10]; // 必须用 delete [] 释放数组 delete [] arr;

4.8.1 指针算术

指针 + 1 不是地址+1,而是“跳到下一个元素”。 arr[i] 等价于 *(arr + i)

0
1
2
3
🧭
int arr[4] = {0, 1, 2, 3}; int* pt = arr; // pt 指向 arr[0] pt++; // pt 现在指向 arr[1] // arr[2] 和 *(arr + 2) 是一样的 cout << *(arr + 2); // 输出 2

4.8.4 动态结构 (箭头)

如果你的指针指向一个结构体 (用 new 创建的),你必须使用箭头运算符 (->) 来访问它的成员。

ps (指针)
->
[ Pizza (在Heap上) ]
struct Pizza { string name; }; // 创建一个动态的 Pizza Pizza* pp = new Pizza; // 使用箭头 -> 访问成员 pp->name = "Supreme"; // 别忘了 delete delete pp;

课时四:高阶类型与现代工具

(覆盖知识点 4.9 - 4.10.3)

4.9 乐高积木拼装

C++允许你像拼乐高一样组合类型:比如“结构数组”(一个数组,每个元素都是一个结构)。

struct Student { string name; int scores[5]; // 数组作为成员 }; // 结构数组 // 创造一个能装 30 个 Student 的数组 Student my_class[30]; // 访问 my_class[0].name = "Alice"; my_class[0].scores[0] = 99;

4.10.1 可伸缩储物柜 (vector)

`vector` 是内置数组的“升级版”。它可以动态增长,用 push_back() 往里添加元素。

99
#include <vector> using namespace std; vector<int> scores; scores.push_back(99); // 添加 99 scores.push_back(100); // 添加 100 // vector 自动变大

4.10.2 安全固定储物柜 (array)

`array` (C++11) 像内置数组一样“长度固定”,但更安全、更现代。

#include <array> using namespace std; // 长度固定的数组 (10个 double) array<double, 10> temp_data; temp_data[0] = 3.14; // temp_data.at(10); // 会安全地报错

编程实践与作业 (4.13)

是时候检验你作为“超级数据容器”管理员的实力了!

练习 1:混合输入处理器 (4.13 练习 1 简化)

任务:

编写一个程序,要求用户先输入年龄(int),再输入包含空格的完整姓名(使用 char 数组)。确保成功读取姓名。

点击查看参考答案
#include <iostream> using namespace std; int main() { int age; const int SIZE = 50; char full_name[SIZE]; cout << "请输入您的年龄: "; cin >> age; // 4.2.5 修复混合输入陷阱:清理输入流中剩余的换行符 cin.get(); cout << "请输入您的全名 (可包含空格): "; // 4.2.4 每次读取一行字符串输入 cin.getline(full_name, SIZE); cout << "您好," << full_name << "!您的年龄是: " << age << endl; return 0; }
练习 2:CandyBar 结构 (4.13 练习 5)

任务:

定义一个 CandyBar 结构,包含品牌(string)、重量(double)和卡路里(int)。初始化并显示其内容。

点击查看参考答案
#include <iostream> #include <string> using namespace std; // 4.4 结构蓝图定义 struct CandyBar { string brand; double weight; int calories; }; int main() { // 4.4.2 C++11 结构初始化 (列表初始化) CandyBar snack = {"Mocha Munch", 2.3, 350}; cout << "零食品牌: " << snack.brand << endl; cout << "重量: " << snack.weight << endl; cout << "卡路里: " << snack.calories << endl; return 0; }
练习 3:动态比萨结构 (4.13 练习 8 简化)

任务:

使用 newPizza 结构动态分配内存,然后获取比萨饼的直径和公司名称,并正确释放内存。

点击查看参考答案
#include <iostream> #include <string> using namespace std; struct Pizza { string company_name; double diameter; }; int main() { // 4.8.4 使用 new 创建动态结构 Pizza * pp = new Pizza; cout << "请输入比萨饼的直径:"; cin >> pp->diameter; // 4.8.4 使用箭头运算符 (->) cin.get(); // 4.2.5 清理换行符 cout << "请输入比萨饼公司的名称:"; getline(cin, pp->company_name); cout << "比萨饼公司: " << pp->company_name << endl; // 4.7.5 必须释放内存! delete pp; return 0; }
练习 4:string 拼接 (4.13 练习 4)

任务:

要求用户输入姓名 (first name) 和姓氏 (last name),然后程序使用 string 对象的拼接特性将它们合并成一个完整的姓名 (格式: last, first),并显示。

点击查看参考答案
#include <iostream> #include <string> using namespace std; int main() { string first_name; string last_name; cout << "Enter your first name: "; cin >> first_name; cout << "Enter your last name: "; cin >> last_name; // 4.3 使用 + 拼接 string 对象 string full_name = last_name + ", " + first_name; cout << "Here's the information in a single string: " << full_name << endl; return 0; }
练习 5:array 声明 (4.12 复习题 17)

任务:

声明一个 C++11 array 对象,包含10个 string 对象。指出所需的头文件。

点击查看参考答案
#include <iostream> #include <string> #include <array> // 4.10.2 引入 array 模板类 using namespace std; int main() { // 4.12 复习题 17:使用 const 指定大小 const int SIZE = 10; // 声明 array 对象 array<string, SIZE> names; cout << "array 对象已创建,大小固定为 " << SIZE << endl; // 初始化第一个元素 names[0] = "Alice"; cout << "第一个名字: " << names[0] << endl; return 0; }

本章知识点总结与复习

核心概念 解释/功能 关键用法/运算符
数组存储多个同种类型的值,通过下标访问。int arr[10];
C-字符串以空字符 (\0) 结尾的 char 数组。char s[] = "word";
string类自动调整大小的字符串对象,支持 + 拼接。string s1 = "hello";
结构 struct存储多个不同类型的值。struct S { ... }; s.a = 1;
指针存储地址的变量。& 取地址; * 解引用
动态内存在自由存储区(堆)分配内存。new 分配; delete 释放
vector / array内置数组的替代品,vector 可变长。vector<int> v;