C/C++ 入门:结构体
变量只能保存一个值,结构体可以把一组相关数据打包成一个整体。
📚 基本概念速读
| 名称 | 定义 | 省流 |
|---|---|---|
| 结构体 | 把多个字段组合成一个自定义类型 | 数据打包 |
| 字段 | 结构体里的成员变量 | 对象的一部分 |
| 成员访问 | 通过 . 或 -> 访问字段 |
拿里面的数据 |
| 结构体变量 | 按结构体类型创建出的对象 | 一个具体数据包 |
| 结构体数组 | 元素类型是结构体的数组 | 一组数据包 |
| 结构体指针 | 保存结构体对象地址的指针 | 指向数据包 |
| 结构体引用 | 结构体对象的别名 | 不拷贝地操作对象 |
| 默认访问权限 | 不写 public、private 时的默认规则 |
struct 默认公开 |
🧩 结构体的基本模型
结构体的核心作用是:把属于同一个概念的数据放在一起。
比如一个学生有姓名、年龄和分数。单独写变量会变散:
1 | string name = "Alice"; |
写成结构体后,这些字段就属于同一个对象:
1 | struct Student { |
可以这样理解:
1 | Student |
结构体让代码从“散落的变量”变成“有名字的数据模型”。
flowchart LR
A[多个相关变量] --> B[struct 类型]
B --> C[结构体变量]
C --> D[通过成员访问读写字段]
🧱 结构体定义
字段
结构体定义的一般形式:
1 | struct 类型名 { |
示例:
1 |
|
Student
是一个新类型,里面有三个字段:name、age、score。
结构体定义末尾的分号不能省略。
初始化
定义好结构体类型后,就可以创建结构体变量。
1 | Student s1 = {"Alice", 18, 92.5}; |
这叫聚合初始化。初始化值会按字段定义顺序依次填进去:
| 字段 | 初始化值 |
|---|---|
name |
"Alice" |
age |
18 |
score |
92.5 |
C++ 里也可以使用 {}:
1 | Student s2{"Bob", 19, 88.0}; |
如果想先创建,再逐个赋值,也可以:
1 | Student s3; |
初学阶段建议优先使用初始化写法,让对象一创建就处于完整状态。
成员访问
普通结构体变量使用 . 访问成员。
1 | Student s{"Alice", 18, 92.5}; |
s.score = 96.0; 的意思是:修改 s
这个学生对象里的 score 字段。
📦 结构体变量与结构体数组
结构体变量
结构体变量和普通变量一样,有类型、有名字、有自己的生命周期。
1 | struct Point { |
这里的 p 是一个 Point
类型变量,它内部有两个整数成员。
结构体之间可以赋值
同类型结构体变量之间可以直接赋值。
1 | Point a{1, 2}; |
这会把 a 的字段值复制给
b。赋值之后,a 和 b
仍然是两个独立对象。
结构体数组
如果要保存多个同类型对象,可以使用结构体数组。
1 | Student students[3] = { |
students[i] 是数组里的一个 Student
对象,所以继续用 . 访问它的字段。
也可以修改数组中某个结构体的成员:
1 | students[1].score = 90.0; |
这只会修改数组下标为 1 的那个学生。
📍 结构体指针
用指针保存结构体地址
结构体变量也有地址,可以用指针保存。
1 | Student s{"Alice", 18, 92.5}; |
这里的 p 保存的是 s 的地址。
-> 成员访问
通过结构体指针访问成员,常用 ->。
1 | cout << p->name << endl; |
p->score 等价于 (*p).score:
1 | (*p).score = 97.0; |
因为 . 的优先级比 * 高,所以必须写成
(*p).score,不能写成 *p.score。
| 写法 | 含义 |
|---|---|
s.score |
通过结构体变量访问字段 |
p->score |
通过结构体指针访问字段 |
(*p).score |
先解引用指针,再访问字段 |
指针传参
结构体指针可以作为函数参数,让函数修改外部结构体对象。
1 | void updateScore(Student* student, double newScore) |
调用时写 &s,表示把 s
的地址传进去。
空指针风险
结构体指针可以是 nullptr,所以使用前要考虑空指针。
1 | Student* p = nullptr; |
如果函数参数允许传空指针,就必须检查:
1 | void printStudent(const Student* student) |
如果函数要求一定传入有效对象,结构体引用通常更直接。
🔗 结构体引用
避免拷贝
结构体可能包含多个字段,值传递会复制整个对象。
1 | void printStudent(Student student) |
如果只是读取,建议使用 const 引用:
1 | void printStudent(const Student& student) |
const Student&
表示:不拷贝整个结构体,也不会通过这个参数修改实参。
修改成员
如果函数需要修改结构体成员,可以使用普通引用。
1 | void updateScore(Student& student, double newScore) |
引用版本调用时直接传 s,不需要写
&s,也不需要在函数内部检查 nullptr。
指针参数和引用参数怎么选
可以先按语义选择:
| 需求 | 推荐写法 | 原因 |
|---|---|---|
| 只读结构体 | const Student& |
避免拷贝,表达不会修改 |
| 必须修改结构体 | Student& |
直接修改实参,语义清楚 |
| 参数可能为空 | Student* |
可以用 nullptr 表达没有对象 |
| 需要和 C 接口交互 | Student* |
C 语言没有引用 |
如果不存在“没有对象”的情况,引用通常比指针更适合做函数参数。
🆚 struct 与 class
默认访问权限
C++ 里的 struct 和 class
很像,都可以有字段和函数。最重要的默认差异是访问权限。
1 | struct StudentStruct { |
struct 成员默认是
public,外部可以直接访问:
1 | StudentStruct a; |
class 成员默认是
private,外部不能直接访问:
1 | StudentClass b; |
如果显式写访问权限,两者可以写出很接近的代码:
1 | struct StudentA { |
使用习惯
实际写 C++ 时,一般习惯这样区分:
| 类型 | 常见用途 |
|---|---|
struct |
更偏简单数据聚合,字段通常直接公开 |
class |
更偏封装行为和不变量,成员通常受访问控制保护 |
这不是语法强制要求,而是代码风格和设计习惯。
⚠️ 常见误区
| 误区 | 正解 |
|---|---|
| 结构体只是 C 语言里的东西 | C++ 也有 struct,而且可以有成员函数 |
| 结构体变量和字段是同一个东西 | 结构体变量是整体,字段是整体的一部分 |
结构体指针可以直接用 . |
指针访问成员通常用 -> |
p->x 和 *p.x 一样 |
p->x 等价于 (*p).x |
| 指针传参一定比引用传参好 | 如果对象必定存在,引用更清楚 |
struct 和 class 完全不同 |
C++ 中主要默认访问权限不同 |
✅ 总结
结构体把一组相关字段组织成一个自定义类型,普通对象用
.访问成员,指针用->,函数传参优先按“是否可空、是否修改、是否避免拷贝”选择指针或引用。
学完结构体后,变量、数组、指针、引用和函数参数就能组合起来表达更真实的数据模型。
Happy Hacking! 🎉