第八章 结构体与共用体
第一节 结构体与共用体的基础
概述
本节内容主要围绕C语言中的结构体(struct)和共用体(union)展开。结构体和共用体是C语言中非常重要的复合数据类型,它们能够将多个不同类型的数据组合成一个整体,极大地增强了程序的数据组织能力和表达能力。通过本节的学习,考生将掌握结构体与共用体的定义、声明、访问以及应用,理解它们的内存布局和区别,能够灵活运用结构体和共用体解决实际编程问题。
学习目标
- 理解结构体和共用体的基本概念及定义方式
- 掌握结构体与共用体的内存分配原理
- 熟悉结构体与共用体的声明、初始化和访问方法
- 掌握结构体数组、指针及嵌套结构体的使用
- 理解共用体的特殊存储方式及适用场景
- 通过实例掌握结构体与共用体的综合应用
核心概念
结构体(struct)
结构体是一种用户自定义的数据类型,它可以将不同类型的变量组合在一起,形成一个整体。结构体中的每个变量称为“成员”或“域”,这些成员可以是不同的数据类型。
结构体的定义形式为:
struct 结构体名 {
数据类型 成员名1;
数据类型 成员名2;
...
};
结构体变量可以用“.”访问成员,也可以通过指针用“->”访问。
共用体(union)
共用体也是一种用户自定义的数据类型,它与结构体类似,但所有成员共用同一块内存空间。共用体的大小等于最大成员的大小,任何时候只能存储一个成员的值。
共用体的定义形式和结构体类似:
union 共用体名 {
数据类型 成员名1;
数据类型 成员名2;
...
};
成员访问
- 对结构体变量,使用
.操作符访问成员。 - 对结构体指针,使用
->操作符访问成员。
内存布局
- 结构体:每个成员占用独立内存,整体大小是所有成员大小之和(考虑对齐)。
- 共用体:所有成员共享同一内存块,整体大小等于最大成员大小。
原理分析
结构体的内存分配原理
结构体的内存是按照成员声明顺序依次分配的,但为了提高访问效率,编译器通常会进行内存对齐,即成员地址按一定规则对齐到特定边界(如4字节对齐)。这可能导致结构体内部产生填充字节。
例如:
struct Example {
char a; // 1字节
int b; // 4字节
};
内存布局可能是:
- a占1字节
- 3字节填充
- b占4字节
总大小为8字节。
共用体的内存分配原理
共用体所有成员共享同一块内存,大小为最大成员所占空间。
例如:
union Example {
int a; // 4字节
float b; // 4字节
char c; // 1字节
};
共用体大小是4字节,所有成员共用这4字节空间。
结构体与共用体的区别
| 特性 | 结构体 | 共用体 |
|---|---|---|
| 内存分配 | 每个成员独立分配内存 | 所有成员共用同一内存空间 |
| 大小 | 成员大小之和(含填充) | 最大成员大小 |
| 同时存储多个成员 | 可以 | 不可以 |
| 用途 | 组织多种数据 | 节省内存,表示同一数据的不同解释 |
详细内容
1. 结构体的定义与声明
结构体的定义包括结构体类型的声明和结构体变量的声明。结构体可以单独定义,也可以在定义时同时声明变量。
struct Person {
char name[20];
int age;
float height;
};
struct Person p1; // 声明结构体变量p1
也可以通过typedef定义结构体别名,简化变量声明:
typedef struct {
char name[20];
int age;
} Person;
Person p2; // 使用别名声明变量
2. 结构体成员访问
访问结构体成员主要用.和->操作符。示例如下:
p1.age = 25;
printf("Name: %s, Age: %d", p1.name, p1.age);
当使用结构体指针时:
struct Person *ptr = &p1;
ptr->age = 30;
3. 结构体的初始化
结构体变量可以在声明时初始化,也可以逐个赋值。
struct Point {
int x;
int y;
};
struct Point pt1 = {10, 20};
4. 结构体数组
结构体数组是结构体类型的数组,用来存储多个结构体变量。
struct Student {
char name[20];
int score;
};
struct Student students[3] = { {"Alice", 90}, {"Bob", 85}, {"Cathy", 88} };
访问数组元素成员:
printf("%s's score: %d", students[1].name, students[1].score);
5. 结构体指针
结构体指针指向结构体变量的地址,便于传递和操作大型结构体。
struct Student *p = &students[0];
printf("%s", p->name);
6. 嵌套结构体
结构体成员也可以是结构体类型,实现复杂数据结构。
struct Date {
int year;
int month;
int day;
};
struct Person {
char name[20];
struct Date birthday;
};
访问嵌套成员:
p1.birthday.year = 2000;
7. 共用体的定义与特点
共用体定义与结构体类似,但所有成员共用内存。
union Data {
int i;
float f;
char str[20];
};
union Data data;
data.i = 10;
注意,共用体在不同成员赋值时会覆盖之前的内容。
8. 共用体的实际用途
共用体常用于节省内存,或实现不同数据类型的“视图”,例如:
- 解析网络数据包
- 存储不同类型的消息
- 实现多态结构
实例分析
实例一:学生信息管理结构体
背景:需要存储学生的姓名、年龄和成绩信息。
#include <stdio.h>
struct Student {
char name[20];
int age;
float score;
};
int main() {
struct Student s1 = {"张三", 20, 88.5};
printf("Name: %s, Age: %d, Score: %.2f\n", s1.name, s1.age, s1.score);
return 0;
}
分析:定义结构体Student,声明变量s1并初始化。通过.访问成员打印学生信息。
结论:结构体便于组织相关数据,易于管理。
实例二:共用体实现多类型数据存储
背景:一个变量可以存储整数、浮点数或字符串。
#include <stdio.h>
#include <string.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
data.i = 100;
printf("data.i = %d\n", data.i);
data.f = 98.6;
printf("data.f = %.2f\n", data.f);
strcpy(data.str, "Hello");
printf("data.str = %s\n", data.str);
return 0;
}
分析:由于共用体共享内存,赋值后前面数据被覆盖。
结论:共用体适合存储多种类型但同一时刻只需一种数据的场景。
实例三:嵌套结构体表示日期和时间
背景:需要存储事件的日期和时间。
#include <stdio.h>
struct Date {
int year;
int month;
int day;
};
struct Time {
int hour;
int minute;
int second;
};
struct Event {
char name[30];
struct Date date;
struct Time time;
};
int main() {
struct Event e = {"会议", {2024, 6, 15}, {14, 30, 0}};
printf("Event: %s\nDate: %d-%d-%d\nTime: %d:%d:%d\n",
e.name, e.date.year, e.date.month, e.date.day, e.time.hour, e.time.minute, e.time.second);
return 0;
}
分析:通过嵌套结构体,清晰表达复杂数据。
结论:嵌套结构体增强数据的层次性和可读性。
常见误区
误区:结构体成员的顺序不影响内存大小
- 正确做法:成员顺序影响内存对齐和填充,合理排序可节省空间。
误区:共用体可以同时存储多个成员的值
- 正确做法:共用体同一时刻只存储一个成员,赋值会覆盖之前数据。
误区:结构体变量初始化时可以省略成员
- 正确做法:初始化应按声明顺序完整赋值,缺省成员值不确定。
误区:结构体指针访问成员用
.而非->- 正确做法:结构体指针访问成员必须用
->操作符。
- 正确做法:结构体指针访问成员必须用
误区:共用体和结构体的声明及变量定义混淆
- 正确做法:结构体和共用体的定义需区分类型声明和变量声明,避免语法错误。
应用场景
- 数据封装:将相关数据组合成结构体,便于管理和传递,如学生信息、坐标点等。
- 节省内存:共用体在嵌入式系统中用来节省内存资源,尤其存储多种状态或数据时。
- 协议解析:网络协议数据包中不同字段解释为不同类型,使用共用体方便解析。
- 多态实现:通过共用体配合标志变量,实现类似多态的功能。
- 复杂数据结构:嵌套结构体构建树、链表、图等复杂结构。
知识拓展
- 结构体对齐与填充:深入了解编译器如何进行内存对齐,利用
#pragma pack调整结构体大小。 - 位域结构体:结构体成员可以定义为位域,控制占用位数,节省空间。
- 联合体与枚举结合:通过枚举标志联合体当前存储的有效成员,实现安全访问。
- 结构体函数指针:结构体成员可以包含函数指针,实现面向对象编程思想。
- 动态结构体:结合指针和动态内存分配,灵活管理结构体数组和链表。
总结回顾
本节系统介绍了C语言中结构体和共用体的基础知识及应用。结构体作为一种组合多种数据类型的复合类型,是组织复杂数据的有力工具;共用体通过共享内存实现多种数据类型共存但互斥存储,适合节约内存和多类型数据解释。掌握结构体和共用体的定义、声明、访问、初始化及内存原理,是深入理解C语言数据结构的基础。通过实例演练,理解结构体数组、指针、嵌套结构体和共用体的实际应用,避免常见误区,能够有效提高编程能力和代码质量。希望考生能结合实际编程多加练习,牢固掌握本节内容,为计算机等级考试二级C语言程序设计打下坚实基础。