# 结构体与共用体:自定义数据类型
## 创建复杂的数据结构
在上一篇文章中,我们学习了内存管理的知识。现在,让我们来学习C语言中用于创建复杂数据结构的工具——结构体和共用体。它们允许我们将不同类型的数据组合在一起,创建自定义的数据类型。
---
## 一、结构体的概念
### 1.1 什么是结构体
结构体是一种用户定义的数据类型,可以将不同类型的数据成员组合在一起。
```c
#include <stdio.h>
#include <string.h>
// 定义结构体
struct Person {
char name[50];
int age;
float height;
};
int main() {
// 声明并初始化结构体变量
struct Person p1;
strcpy(p1.name, "张三");
p1.age = 25;
p1.height = 1.75f;
// 访问结构体成员
printf("姓名:%s\n", p1.name);
printf("年龄:%d\n", p1.age);
printf("身高:%.2f\n", p1.height);
return 0;
}
```
### 1.2 结构体的定义
```c
// 语法
struct 结构体名 {
数据类型 成员名1;
数据类型 成员名2;
// ...
};
// 示例
struct Student {
int id;
char name[50];
float score;
};
struct Point {
int x;
int y;
};
```
### 1.3 结构体的初始化
```c
#include <stdio.h>
#include <string.h>
struct Person {
char name[50];
int age;
float height;
};
int main() {
// 方式1:逐个成员初始化
struct Person p1;
strcpy(p1.name, "张三");
p1.age = 25;
p1.height = 1.75f;
// 方式2:使用初始化列表
struct Person p2 = {"李四", 30, 1.80f};
// 方式3:指定成员初始化(C99标准)
struct Person p3 = {
.name = "王五",
.height = 1.72f,
.age = 28
};
return 0;
}
```
---
## 二、结构体的使用
### 2.1 结构体数组
```c
#include <stdio.h>
struct Student {
int id;
char name[50];
float score;
};
int main() {
// 定义结构体数组
struct Student students[3] = {
{101, "张三", 95.5f},
{102, "李四", 88.0f},
{103, "王五", 92.3f}
};
// 遍历结构体数组
for (int i = 0; i < 3; i++) {
printf("学生%d:\n", i + 1);
printf(" ID:%d\n", students[i].id);
printf(" 姓名:%s\n", students[i].name);
printf(" 成绩:%.1f\n", students[i].score);
}
return 0;
}
```
### 2.2 结构体指针
```c
#include <stdio.h>
#include <string.h>
struct Person {
char name[50];
int age;
};
int main() {
struct Person p = {"张三", 25};
struct Person *ptr = &p;
// 使用指针访问结构体成员
printf("姓名:%s\n", ptr->name);
printf("年龄:%d\n", ptr->age);
// 修改结构体成员
strcpy(ptr->name, "李四");
ptr->age = 30;
printf("修改后:\n");
printf("姓名:%s\n", p.name);
printf("年龄:%d\n", p.age);
return 0;
}
```
### 2.3 结构体作为函数参数
```c
#include <stdio.h>
struct Point {
int x;
int y;
};
// 值传递
void printPoint(struct Point p) {
printf("Point: (%d, %d)\n", p.x, p.y);
}
// 指针传递
void movePoint(struct Point *p, int dx, int dy) {
p->x += dx;
p->y += dy;
}
int main() {
struct Point pt = {3, 4};
printPoint(pt);
movePoint(&pt, 2, -1);
printPoint(pt);
return 0;
}
```
---
## 三、嵌套结构体
### 3.1 结构体嵌套
```c
#include <stdio.h>
// 定义日期结构体
struct Date {
int year;
int month;
int day;
};
// 定义学生结构体,包含日期结构体
struct Student {
char name[50];
struct Date birthday;
float score;
};
int main() {
struct Student s = {
"张三",
{2000, 5, 15},
95.5f
};
printf("姓名:%s\n", s.name);
printf("生日:%d年%d月%d日\n", s.birthday.year, s.birthday.month, s.birthday.day);
printf("成绩:%.1f\n", s.score);
return 0;
}
```
### 3.2 结构体链表
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义链表节点
typedef struct Node {
int data;
struct Node *next;
} Node;
// 创建新节点
Node* createNode(int data) {
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 打印链表
void printList(Node *head) {
Node *current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
int main() {
// 创建链表
Node *head = createNode(1);
head->next = createNode(2);
head->next->next = createNode(3);
printf("链表:");
printList(head);
return 0;
}
```
---
## 四、共用体的概念
### 4.1 什么是共用体
共用体(Union)是一种特殊的数据类型,所有成员共享同一块内存空间。
```c
#include <stdio.h>
// 定义共用体
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
printf("共用体大小:%zu bytes\n", sizeof(data));
// 使用整型
data.i = 100;
printf("data.i = %d\n", data.i);
// 使用浮点型(会覆盖之前的值)
data.f = 3.14f;
printf("data.f = %.2f\n", data.f);
printf("data.i = %d\n", data.i); // 值已改变
// 使用字符串
strcpy(data.str, "Hello");
printf("data.str = %s\n", data.str);
printf("data.i = %d\n", data.i); // 值已改变
return 0;
}
```
### 4.2 共用体的特点
| 特性 | 结构体 (struct) | 共用体 (union) |
|------|---------------|---------------|
| **内存分配** | 成员各自独立分配内存 | 成员共享同一块内存 |
| **内存大小** | 所有成员大小之和 | 最大成员的大小 |
| **成员访问** | 可以同时访问所有成员 | 同一时刻只能访问一个成员 |
| **用途** | 表示一个对象的多个属性 | 节省内存,类型转换 |
---
## 五、typedef的使用
### 5.1 简化结构体定义
```c
#include <stdio.h>
// 使用typedef简化结构体定义
typedef struct {
char name[50];
int age;
float height;
} Person; // Person现在是一个类型名
int main() {
// 直接使用Person作为类型
Person p = {"张三", 25, 1.75f};
printf("姓名:%s,年龄:%d,身高:%.2f\n", p.name, p.age, p.height);
return 0;
}
```
### 5.2 typedef的常见用法
```c
#include <stdio.h>
// 为基本类型创建别名
typedef int Integer;
typedef float Real;
// 为指针类型创建别名
typedef int* IntPtr;
typedef char* String;
// 为数组类型创建别名
typedef int IntArray[10];
int main() {
Integer num = 42;
Real pi = 3.14f;
String str = "Hello";
IntArray arr = {1, 2, 3, 4, 5};
printf("num = %d\n", num);
printf("pi = %.2f\n", pi);
printf("str = %s\n", str);
return 0;
}
```
---
## 六、位域
### 6.1 什么是位域
位域允许我们按位来分配结构体成员的内存空间。
```c
#include <stdio.h>
// 使用位域定义结构体
struct Flags {
unsigned int is_active : 1; // 1位
unsigned int is_admin : 1; // 1位
unsigned int has_access : 1; // 1位
unsigned int reserved : 29; // 29位(总共32位)
};
int main() {
struct Flags f;
printf("Flags大小:%zu bytes\n", sizeof(f));
f.is_active = 1;
f.is_admin = 0;
f.has_access = 1;
printf("is_active = %u\n", f.is_active);
printf("is_admin = %u\n", f.is_admin);
printf("has_access = %u\n", f.has_access);
return 0;
}
```
### 6.2 位域的应用场景
```c
#include <stdio.h>
// 颜色表示(使用位域)
struct Color {
unsigned int red : 8;
unsigned int green : 8;
unsigned int blue : 8;
unsigned int alpha : 8;
};
int main() {
struct Color c = {255, 128, 64, 255};
printf("红色:%u\n", c.red);
printf("绿色:%u\n", c.green);
printf("蓝色:%u\n", c.blue);
printf("透明度:%u\n", c.alpha);
// 将结构体转换为32位整数
unsigned int color_value = *(unsigned int *)&c;
printf("颜色值:0x%08X\n", color_value);
return 0;
}
```
---
## 七、枚举类型
### 7.1 什么是枚举
枚举(Enumeration)是一种用户定义的整数类型,用于表示一组命名常量。
```c
#include <stdio.h>
// 定义枚举类型
enum Weekday {
Monday, // 0
Tuesday, // 1
Wednesday, // 2
Thursday, // 3
Friday, // 4
Saturday, // 5
Sunday // 6
};
int main() {
enum Weekday today = Wednesday;
printf("今天是星期%d\n", today);
// switch语句中使用枚举
switch (today) {
case Monday:
case Tuesday:
case Wednesday:
case Thursday:
case Friday:
printf("工作日\n");
break;
case Saturday:
case Sunday:
printf("周末\n");
break;
}
return 0;
}
```
### 7.2 枚举的自定义值
```c
#include <stdio.h>
// 自定义枚举值
enum Status {
ERROR = -1,
SUCCESS = 0,
WARNING = 1,
INFO = 2
};
// 带位运算的枚举
enum Permission {
READ = 1 << 0, // 0001
WRITE = 1 << 1, // 0010
EXECUTE = 1 << 2 // 0100
};
int main() {
printf("ERROR = %d\n", ERROR);
printf("SUCCESS = %d\n", SUCCESS);
// 使用位运算组合权限
int permissions = READ | WRITE;
printf("权限组合:%d\n", permissions);
if (permissions & READ) {
printf("有读权限\n");
}
return 0;
}
```
---
## 八、实战练习
### 练习1:学生信息管理系统
```c
#include <stdio.h>
#include <string.h>
#define MAX_STUDENTS 100
struct Student {
int id;
char name[50];
float score;
};
void addStudent(struct Student students[], int *count) {
if (*count >= MAX_STUDENTS) {
printf("学生数量已达上限\n");
return;
}
struct Student s;
printf("请输入学生ID:");
scanf("%d", &s.id);
printf("请输入学生姓名:");
scanf("%s", s.name);
printf("请输入学生成绩:");
scanf("%f", &s.score);
students[*count] = s;
(*count)++;
printf("学生信息添加成功\n");
}
void printStudents(struct Student students[], int count) {
printf("\n学生列表:\n");
printf("ID\t姓名\t成绩\n");
for (int i = 0; i < count; i++) {
printf("%d\t%s\t%.1f\n", students[i].id, students[i].name, students[i].score);
}
}
int main() {
struct Student students[MAX_STUDENTS];
int count = 0;
int choice;
do {
printf("\n学生信息管理系统\n");
printf("1. 添加学生\n");
printf("2. 显示学生列表\n");
printf("3. 退出\n");
printf("请选择:");
scanf("%d", &choice);
switch (choice) {
case 1:
addStudent(students, &count);
break;
case 2:
printStudents(students, count);
break;
case 3:
printf("退出系统\n");
break;
default:
printf("无效选择\n");
}
} while (choice != 3);
return 0;
}
```
### 练习2:二叉树节点结构
```c
#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode {
int data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
TreeNode* createNode(int data) {
TreeNode *node = (TreeNode *)malloc(sizeof(TreeNode));
node->data = data;
node->left = NULL;
node->right = NULL;
return node;
}
void inorderTraversal(TreeNode *root) {
if (root == NULL) return;
inorderTraversal(root->left);
printf("%d ", root->data);
inorderTraversal(root->right);
}
int main() {
// 创建二叉树
TreeNode *root = createNode(1);
root->left = createNode(2);
root->right = createNode(3);
root->left->left = createNode(4);
root->left->right = createNode(5);
printf("中序遍历:");
inorderTraversal(root);
printf("\n");
return 0;
}
```
### 练习3:共用体实现类型转换
```c
#include <stdio.h>
union FloatBits {
float f;
unsigned int i;
};
void printFloatBits(float num) {
union FloatBits fb;
fb.f = num;
printf("浮点数:%.6f\n", num);
printf("二进制表示:");
for (int j = 31; j >= 0; j--) {
printf("%d", (fb.i >> j) & 1);
if (j == 31 || j == 23) printf(" ");
}
printf("\n");
}
int main() {
printFloatBits(3.14f);
printFloatBits(-2.5f);
printFloatBits(0.0f);
return 0;
}
```
---
## 九、总结与延伸
### 本节重点回顾
1. **结构体**:将不同类型数据组合在一起的自定义类型
2. **结构体定义**:`struct 结构体名 { 成员列表 };`
3. **结构体访问**:使用`.`运算符,指针使用`->`运算符
4. **结构体数组**:存储多个结构体变量
5. **嵌套结构体**:结构体中包含另一个结构体
6. **共用体**:成员共享同一块内存空间
7. **typedef**:为类型创建别名
8. **位域**:按位分配内存空间
9. **枚举**:表示一组命名常量
### 下节预告
下一篇文章我们将学习《文件操作:数据的持久化存储》,深入理解如何读写文件。
---
📌 **知识点卡片**
- 结构体:`struct Name { 成员列表 };`
- 结构体访问:`.`和`->`
- 共用体:成员共享内存,同一时刻只能访问一个成员
- typedef:为类型创建别名
- 位域:按位分配内存
- 枚举:一组命名常量
💡 **小技巧**
- 使用typedef简化结构体定义
- 结构体作为函数参数时,通常使用指针传递
- 共用体可用于类型转换
- 位域适合存储标志位和状态信息
- 枚举提高代码可读性
⚠️ **注意事项**
- 共用体成员共享内存,修改一个会影响其他成员
- 结构体大小可能因对齐而大于成员大小之和
- 枚举本质上是整数类型
- 位域的位顺序依赖于系统架构
---
> 如果你对结构体和共用体有任何疑问,欢迎在评论区留言讨论!下一篇见!