定义结构体的格式是 struct 结构体名 { 成员列表 };——注意末尾那个分号,它是本章第一大坑。定义只是画图纸,不占内存;图纸画好可以反复用(造很多个变量)。成员列表跟普通变量声明长得一样,唯一区别是它们被包在花括号里、有了统一的归属。
8.1 说了"结构体是什么、为什么需要"。这一节落地语法:模板到底怎么写。用到的零件你全都学过——类型来自第二章,成员名要守 2.3.1 的标识符规则,花括号是 3.x/4.x 就见过的"圈定范围"符号(作用域边界,6.4),分号是 1.3.2 的三条铁律之一。下一节 8.1.2 拿这张图纸去造变量。
一、标准写法:先画模板,再单独造变量
struct Student // ① struct + 结构体名
{
char name[20]; // ② 成员列表:每一行都是一个普通的变量声明
int age; // 末尾分号照常写
double score;
}; // ③ 整个定义结束,还要再加一个分号!
拆成三部分看:
| 部分 | 内容 | 讲究 |
|---|---|---|
| 关键字 + 名字 | struct Student |
Student 是新造出来的类型名,从此可以像 int 一样用 |
| 成员列表 | { ... } |
里面每一行都是普通变量声明;成员名不能重复,可以和外面的变量重名(那样会被"遮住",见 6.4 就近遮蔽) |
| 结尾分号 | }; |
必须有。漏了就报错,而且报错行往往不在这一行 |
为什么末尾要分号?把
struct Student { ... }理解成一句完整的声明:它要告诉编译器"有这么一种类型"。C++ 里凡是"声明"都以分号收尾——就像int a;那样。记忆法:花括号圈完内容,分号宣布结束。
二、三种写法:什么时候造变量
写法 1:先定义类型,之后随时造变量(最常用)
struct Student {
char name[20];
int age;
double score;
}; // ← 类型定义完毕
int main() {
Student s1; // 用的时候再一个个造
Student s2;
return 0;
}
推荐这种。类型和变量分开,模板只写一次、变量用到再造,最清楚。
写法 2:定义类型的同时顺便造变量
struct Student {
char name[20];
int age;
double score;
} s1, s2; // ← 分号前多了变量名
s1, s2 是紧跟类型定义的变量。语法合法,但不推荐:将来想再加变量,就得回去改定义那一行,容易乱。
写法 3:匿名结构体(连名字都不起)
struct {
char name[20];
int age;
double score;
} s1; // 一次性用品:只有 s1 能用,再想造 s2 就没名字可用了
特殊场合才用(比如某个嵌套里只需要一个成员)。新手别用——你没法再声明第二个同类型的变量。
三、"定义类型"和"定义变量"是两件事
这是本节最需要分清的一点:
| 动作 | 代码 | 干什么 | 占内存吗 |
|---|---|---|---|
| 定义类型 | struct Student { ... }; |
告诉编译器"有这么一种模板" | 不占 |
| 定义变量 | Student s1; |
照着模板真正分配一块空间 | 占(28 字节) |
打个比方:struct Student 是表格模板(印在纸上),Student s1 是照着模板打印出来的一张具体表格(填了张三的数据)。你可以印一万张,模板本身还是那张纸。
struct Student {
char name[20];
int age;
double score;
}; // 到这一行为止,内存里一个字节都没分配
Student s1; // 现在才有了第一块 28 字节的空间
Student s2; // 又一块
Student arr[30]; // 一下子 30 块(8.4)
四、取名与排版约定
教材和多数工程都遵循这几条,照着写就对了:
- 结构体名首字母大写(
Student、Point、Date)——用来和变量名(s1、p)区分,一眼看出"这是模板"。 - 成员名用有意义的小写(
name/age/score,而不是a/b/c)——结构体的意义就在于让人看懂"一条记录由什么组成"。 - 成员按类型从小到大排——这不只是为了好看,能省内存(8.1.5 的"对齐"会讲清楚)。
- 结尾分号单独顶格写,别藏在成员后面。
关于 typedef:你可能会在教材或老代码里看到 typedef struct Student { ... } Student;,那是给结构体起别名,8.2 专门讲。
五、动手 Demo:把图纸画出来,看它占不占内存
#include <iostream>
#include <cstring>
using namespace std;
// ① 先定义类型(此刻不占内存)
struct Point {
int x;
int y;
};
struct Student {
char name[20];
int age;
double score;
};
int main() {
// ② 看"类型"本身的大小
cout << "sizeof(Point) = " << sizeof(Point) << endl;
cout << "sizeof(Student) = " << sizeof(Student) << endl;
// ③ 造变量,此刻才占用内存
Point p1;
p1.x = 3; p1.y = 4;
cout << "p1 = (" << p1.x << ", " << p1.y << ")" << endl;
Student s1;
strcpy(s1.name, "张三");
s1.age = 18; s1.score = 92.5;
cout << s1.name << " " << s1.age << " " << s1.score << endl;
return 0;
}
看到什么算成功:依次输出 sizeof(Point) = 8、sizeof(Student) = 32、p1 = (3, 4)、张三 18 92.5。
读结果:Point 两个 int 恰好 8 字节;Student 是 32(20+4+8,三种成员正好排得下)。但并不总是这么"正好"——有些组合会凭空多出几个字节的空白,那就是"对齐",下一节 8.1.5 揭晓。
30 秒实验:故意把定义末尾的分号删掉:
struct Point {
int x;
int y;
} // ← 少了一个分号
编译,记下报错的行号和信息。你会发现报错常常指向下一行的 struct Student——这正是 1.3.2 说过的"漏分号,报错常报在下一行"的典型病例。
六、常见误区
- "末尾分号可写可不写"——必须有。少了它,编译器会把下一个
struct当成"接着声明变量",报出一堆莫名其妙的错。 - "定义完就能用了"——定义完只得到了类型名。要用
Student s1;造出变量才能存数据(8.1.2)。sizeof(Student)能问大小,但那不是"有了一块你的内存"。 - "成员列表里可以给默认值"——老标准里不行(
int age = 18;在 C++11 之前不允许)。新版 C++ 允许了,但教材环境(Dev-C++ 的老 MinGW)多半不支持,老老实实在初始化里填(8.1.3)。 - "结构体名和变量名可以一样"——语法上勉强能过,但极易看晕。按约定:类型首字母大写、变量小写。
- "匿名结构体更省事"——它没法复用:只能在那一次声明里定义变量,之后再也找不到这个类型名。除非你确定只用一次,否则别用。
自测三问(点开对答案)
1. 写出"定义一种叫 `Book` 的结构体,含书名、作者、价格"的代码(注意分号)。——`struct Book { char title[50]; char author[30]; double price; };` 2. `struct Student { ... };` 这一句执行完,内存里多了什么?——什么都没多。它只登记了**类型**;分配内存发生在 `Student s1;` 这类语句。 3. 为什么推荐"先定义类型、用到时再造变量"?——类型只写一次、集中好维护;变量按需创建,用途清楚。在定义处顺手造变量会让那一行变长难改。- ☐ 8.1.2 结构体变量(下一节)
相关笔记
- 结构体 —— 结构体是什么、为什么需要
- 结构体变量 —— 下一节:拿图纸造变量
- 结构体的大小 —— 为什么实际大小 ≠ 成员相加
- 认识C++代码结构 —— 漏分号为什么报在下一行
- 🔷 C++基础 · 索引 —— 全课路线图