🏗️ 一、结构体 vs 数组:数据组织的升级 🧺
核心区别
数据类型 | 特点 | 示例场景 |
---|---|---|
数组 | 相同类型数据的集合 | 存储 5 个学生的年龄(int 数组) |
结构体 | 不同类型数据的集合 | 存储 1 个学生的完整信息(姓名 + 年龄 + 成绩) |
一句话总结
结构体是用 struct
关键字定义的 “复合数据类型”,可以把不同类型的数据(如 int
、char
、float
)打包成一个整体。
二、结构体定义:语法格式与位置 📑
1. 定义格式(主函数外定义)
// 结构体标签:Student(相当于自定义类型名)
struct Student {
char name[20]; // 成员变量1:字符数组(姓名)
int age; // 成员变量2:整数(年龄)
float score; // 成员变量3:浮点数(成绩)
}; // 分号不能少!
部分 | 说明 |
---|---|
struct | 关键字,声明这是一个结构体定义 |
Student | 结构体标签(类型名),后续声明变量需用 struct Student 或 typedef 简化 |
{ } | 包裹成员列表,每个成员以分号结尾 |
末尾分号 ; | 结构体定义的结束标志,漏写会导致编译错误 |
2. 定义时直接声明变量(全局 / 局部)
// 全局结构体变量(在主函数外声明)
struct Student {
char name[20]; // 成员变量1:字符数组(姓名)
int age; // 成员变量2:整数(年龄)
float score; // 成员变量3:浮点数(成绩)
}stu1; // stu1 是结构体变量,也是全局变量。
//或者在定义结构体之后,写:struct Student stu1;
int main() {
// 局部结构体变量(在主函数内声明)
struct Student stu2;
return 0;
}
🚨 注意事项
- 结构体标签:定义时必须写
struct Student
。 - 分号必填:结构体定义末尾的分号容易漏写,会导致编译错误。
三、结构体初始化📦
1. 按顺序初始化(最常用)
struct Student {
char name[20];
int age;
float score;
};
// 定义全局变量时直接初始化(按成员顺序赋值)
struct Student stu = {
"小明", // 对应 char name[20](字符数组初始化)
18, // 对应 int age
85.5 // 对应 float score
};
🔍 初始化规则
- 花括号内的值按结构体成员定义的顺序依次赋值。
- 字符数组:用双引号字符串直接初始化(如
"小明"
),长度不超过数组声明的大小(20)。 - 基本类型:直接写对应类型的值(整数、浮点数)。
2. 指定成员初始化
struct Student stu = {
.age = 20, // 显式指定初始化age成员
.name = "小红", // 初始化name成员
.score = 90.0 // 初始化score成员(顺序可乱)
};
- 无需严格按成员顺序,可读性更强,避免因成员顺序变化导致的错误。
- 未指定的成员自动初始化为 0(数值型)或空字符(字符型)。
3. 嵌套结构体初始化(成员是另一个结构体)
// 先定义地址结构体
struct Address {
char city[20];
char street[30];
};
// 学生结构体包含地址结构体成员
struct Student {
char name[20];
int age;
struct Address addr; // 成员是另一个结构体
};
// 初始化嵌套结构体
struct Student stu = {
"小刚",
19,
{ // 嵌套结构体的初始化括号
"北京市",
"长安街10号"
}
};
🚨 易错
- 多层括号:嵌套结构体初始化时,内层结构体要用额外的
{}
包裹,否则编译报错。 - 类型匹配:初始化值的类型必须与成员类型一致(如
name
是数组,需要用字符串常量初始化)。
四、结构体变量的使用🔑
1. 通过变量名访问(‘ . ' 操作符)
#include <stdio.h>
// 定义结构体类型
struct Student {
char name[20];
int age;
float score;
};
// 全局结构体变量(定义在所有函数之外)
struct Student stu = { "小明", 18, 85.5 };
int main() {
// 通过全局变量名 `stu` 访问成员(. 操作符)
printf("全局变量访问:\n");
printf("姓名: %s\n", stu.name); // 输出:小明
printf("年龄: %d\n", stu.age); // 输出:18
printf("成绩: %.1f\n", stu.score); // 输出:85.5
// 修改全局变量的成员
stu.age = 19;
printf("\n修改后年龄: %d\n", stu.age); // 输出:19
return 0;
}
关键点:
- 作用域:
stu
是全局变量,在整个文件内可见(包括main
函数)。 - 访问格式:直接通过
变量名.成员名
操作,无需额外声明。 - 内存位置:全局变量存储在数据段,生命周期贯穿程序始终。
2. 通过指针访问(->
操作符)
struct Student* p = &stu; // 定义结构体指针,指向stu
printf("姓名:%s\n", p->name); // 用 -> 访问成员(等价于 (*p).name)
printf("年龄:%d\n", p->age);
🚨 注意事项
- 操作符区别:
- 结构体变量用
.
:如stu.name
- 结构体指针用
->
:如p->name
(不能写成p.name
)
- 结构体变量用
五、结构体定义的 2种写法📝
1. 标准写法(最清晰)
// 定义结构体标签
struct Student {
char name[20];
int age;
};
// 声明变量
struct Student stu;
2. 用 typedef 简化写法(推荐)
// 用 typedef 定义结构体别名(替代 struct Student)
typedef struct {
char name[20];
int age;
float score;
} Student; // 别名:Student 等价于 struct { ... }
六、结构体成员是另一个结构体:嵌套案例 🏘️
🌰 案例:学生结构体包含地址结构体
// 定义地址结构体(成员是字符串)
typedef struct {
char city[20];
char street[30];
} Address;
// 定义学生结构体,包含地址结构体成员
typedef struct {
char name[20];
int age;
Address home; // 成员是Address结构体
} Student;
int main() {
// 初始化嵌套结构体
Student stu = {
"小孙",
20,
{ // 初始化Address成员
"徐州市",
"欣欣路"
}
};
// 访问嵌套成员
printf("家庭地址:%s %s\n", stu.home.city, stu.home.street);
return 0;
}
🚨 关键点
- 嵌套成员的访问:通过多层
.
操作符,如stu.home.city
。 - 初始化顺序:先外层结构体成员,再内层结构体的
{}
包裹初始化值。
七、必看!结构体五大易错点 🚫
-
漏写 struct 关键字
❌ 错误:Student stu;
(需先typedef
或写struct Student
)
✅ 正确:struct Student stu;
或typedef struct Student Student;
-
结构体定义末尾漏分号
❌ 错误:struct Student { ... }
(末尾无分号)
✅ 正确:struct Student { ... };
(分号必填) -
指针访问成员用错操作符
❌ 错误:p.name
(指针需用->
)
✅ 正确:p->name
或(*p).name
-
嵌套初始化少括号
❌ 错误:{ "小明", 18, "北京市", "长安街" }
(缺少内层{}
)
✅ 正确:{ "小明", 18, { "北京市", "长安街" } }
-
给数组成员直接赋值
❌ 错误:stu.name = "小红";
(数组不能直接赋值)
✅ 正确:strcpy(stu.name, "小红");
(需包含头文件<string.h>
) -
结构体成员(如:
name
/age
/score
):- 它们是结构体类型的字段,既不是全局变量也不是局部变量
- 属于结构体
Student
的定义部分,作用域仅限于结构体实例 - 必须通过结构体变量(如
stu
)访问,例如:stu.name
、stu.age
🌟 总结:结构体知识点
概念 | 说明 |
---|---|
定义格式 | struct 标签 { 成员列表; }; ,末尾分号必填 |
初始化 | 按顺序或指定成员初始化,嵌套结构体用多层{} |
访问成员 | 变量用 ‘ . ',指针用 -> |
嵌套成员 | 成员可以是另一个结构体,初始化时内层用{} 包裹 |
typedef | 简化写法,避免重复写struct ,如typedef struct Student Student; |
💡 建议
- 多用 typedef:定义结构体时用
typedef
,避免每次写struct
。 - 初始化顺序对应:按成员顺序写初始化值,或显式指定成员(如
.age = 20
),可读性更强。
结构体是 C 语言组织复杂数据的核心,掌握后能让代码更有条理。
动手写几个案例,很快就能上手! 🚀