结构体变量的声明和普通变量一模一样:Student s1;。真正的新东西是访问成员的那个点号 .——s1.name、s1.age。记住一句口诀:"变量点成员",点号左边是哪个变量,右边是哪一格。另外结构体变量有一条数组羡慕不来的特权:同类型的两个结构体变量可以整体赋值 s2 = s1;。
8.1.1 画好了图纸(类型),这一节照着图纸造房子(变量)并开门进去(访问成员)。点号 . 是新符号,但它背后的思路你熟悉:就像 7.2.2 用 a[i] 说"整排里的第 i 格",这里用 s.age 说"这一包里的 age 那一格"。下一节 8.1.3 讲"造出来的时候怎么直接填好值"。
一、声明:跟普通变量一个模子
Student s1; // 一个学生
Student s2; // 又一个
Student s3, s4; // 一行造两个,也合法
看到 Student s1; 要能自动翻译成:"按 Student 的图纸,分配一块 32 字节的空间,整块叫 s1"。
结构体变量可以声明在三个常规位置:
| 位置 | 代码 | 特点 |
|---|---|---|
| 函数里(局部) | int main() { Student s1; } |
随函数生灭(6.7.1 的 auto);未初始化=成员全是垃圾值 |
| 函数外(全局) | 在所有函数之前 Student g; |
活到程序结束;成员自动为 0;全局变量代价见 6.4 |
| 结构体嵌套时 | 作为另一个结构体的成员 | 8.1.4 的内容 |
重要区别:全局的结构体变量成员自动清零,局部的不会(和 2.3.2 的"未初始化=垃圾值"完全一致,只是从一格变成了一包)。 等一会儿:教材常见写法是把变量声明在
main里,那就一定是垃圾值,必须先赋值再用。
二、访问成员:点号 .
Student s1;
s1.age = 18; // 写:给 age 那一格填 18
s1.score = 92.5; // 写:给 score 那一格填 92.5
strcpy(s1.name, "张三"); // 写:字符数组成员要用 strcpy(7.3)
cout << s1.age << endl; // 读:把 age 那一格的内容拿出来
格式固定为:
结构体变量名 . 成员名
. 是"成员访问运算符",读作"的"。s1.age 读作"s1 的 age",正好就是它的语义。
一句口诀记牢:"变量点成员"(
s1.age)。 反过来写就错:Student.age、age.s1全都不对。
不同成员要有不同的填值方式
这是新手最容易混的地方,按成员类型分三档:
| 成员类型 | 填值写法 | 为什么 |
|---|---|---|
普通类型(int/double) |
s1.age = 18; |
直接 = |
字符数组(char name[20]) |
strcpy(s1.name, "张三"); |
数组不能整体赋值(7.3 的铁律),要用 strcpy |
| 输入 | cin >> s1.age; |
和普通变量一样;但字符数组成员要用 cin.getline(遇空格不停) |
// ❌ 三个典型错误
s1.name = "张三"; // 错:数组名是地址常量,不能当左值(7.5)
s1 = {"张三", 18, 92.5}; // 错:这句是"整体赋值",右边必须已经是同类型变量(见下节)
s1.age = "18"; // 错:类型不匹配,int 格放不下字符串
三、结构体变量的"特权":整体赋值
普通数组有一条遗憾:不能整体赋值。
int a[5] = {1,2,3}, b[5];
b = a; // ❌ 编译错误:数组名不是可修改的左值(7.5)
结构体可以:
Student s1, s2;
strcpy(s1.name, "张三"); s1.age = 18; s1.score = 92.5;
s2 = s1; // ✅ 合法!把 s1 的 32 字节整块复制到 s2
cout << s2.name << endl; // 张三
它的含义是"逐字节整块复制"(和 s2.name、s2.age、s2.score 一项项赋值等效),比手写三行省事,也更不容易漏项。
三条注意:
1. 两边必须是同一个结构体类型。Student s; Point p; p = s; 报错(编译器按类型对不上)。
2. 这是"复制",不是"绑定"。s2 = s1; 之后改 s2,s1 不受影响——它们各自占用独立的 32 字节。
3. 这条特权只对结构体(和后面的类)有,数组、char[] 都没有。
四、动手 Demo:造两个学生,试试整体赋值
#include <iostream>
#include <cstring>
using namespace std;
struct Student {
char name[20];
int age;
double score;
};
int main() {
Student s1, s2;
// ① 逐项填 s1
strcpy(s1.name, "张三");
s1.age = 18;
s1.score = 92.5;
// ② 整体复制给 s2
s2 = s1;
cout << "复制后 s2:" << s2.name << " " << s2.age << " " << s2.score << endl;
// ③ 验证"复制"不是"绑定":改 s2,看 s1 是否受影响
s2.age = 99;
strcpy(s2.name, "李四");
cout << "改 s2 后 s1:" << s1.name << " " << s1.age << endl;
cout << "改 s2 后 s2:" << s2.name << " " << s2.age << endl;
// ④ 看看未初始化的成员
Student s3;
cout << "未初始化的 s3.age = " << s3.age << "(垃圾值)" << endl;
return 0;
}
看到什么算成功:
- 复制后 s2:张三 18 92.5
- 改完 s2 后,s1 仍然是 张三 18(证明是复制,各管各的)
- 最后一行 s3.age 是个随机数(每次运行可能不同)
30 秒实验:把 Student s3; 挪到 main 函数外面(变成全局变量),重新运行——s3.age 变成 0。全局自动清零、局部是垃圾值,和第二章变量规则完全一致。
五、常见误区
- "结构体变量必须初始化才能声明"——不用。但不初始化就不能读(局部变量里全是垃圾值)。想干净:要么声明时初始化(8.1.3),要么先全部赋值再用。
- "
s1 = "张三"能直接给整个结构体赋值"——不能。右边必须是同类型的结构体变量。字符串只是其中一个成员的值,得进到s1.name那一格。 - "访问成员用
->"——->是指针专用的(8.3.2),普通变量一律用.。口诀:变量点、指针箭头。 - "
s2 = s1;之后 s2 和 s1 是同一块内存"——不是。它是复制(值语义)。想让两个名字指向同一块,那是指针或引用干的事(7.8)。
自测三问(点开对答案)
1. 访问结构体变量成员的写法?举三种不同成员类型的填值例子。——`变量名.成员名`。普通类型 `s.age = 18;`;字符数组成员 `strcpy(s.name, "张三");`;输入 `cin >> s.age;`。 2. `s2 = s1;` 是复制还是绑定?之后改 `s2` 会影响 `s1` 吗?——复制(整块 32 字节搬过去),**不影响**,两者各自独立。 3. 为什么 `s1.name = "张三";` 是错的,而 `s2 = s1;` 是对的?——`s1.name` 是**数组名**(地址常量,不能当左值);而 `s1`/`s2` 是**结构体变量**,结构体整体赋值是语言允许的。- ☐ 8.1.3 结构体成员及初始化(下一节)
相关笔记
- 结构体定义 —— 上一节:图纸怎么写
- 结构体成员及初始化 —— 下一节:一次填好所有成员
- 指针与数组 —— 数组名为什么不能整体赋值
- 变量的声明及赋值 —— 未初始化=垃圾值
- 🔷 C++基础 · 索引 —— 全课路线图