news 2026/6/20 0:07:45

12-结构体与共用体:自定义数据类型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
12-结构体与共用体:自定义数据类型


# 结构体与共用体:自定义数据类型

## 创建复杂的数据结构

在上一篇文章中,我们学习了内存管理的知识。现在,让我们来学习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简化结构体定义
- 结构体作为函数参数时,通常使用指针传递
- 共用体可用于类型转换
- 位域适合存储标志位和状态信息
- 枚举提高代码可读性

⚠️ **注意事项**
- 共用体成员共享内存,修改一个会影响其他成员
- 结构体大小可能因对齐而大于成员大小之和
- 枚举本质上是整数类型
- 位域的位顺序依赖于系统架构

---

> 如果你对结构体和共用体有任何疑问,欢迎在评论区留言讨论!下一篇见!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/6 2:03:44

LoRA 微调后长文本失效?深挖 Self-Attention 矩阵秩缺失的数学真相

LoRA 微调后长文本失效&#xff1f;深挖 Self-Attention 矩阵秩缺失的数学真相前言 你在生产环境中是否遇到过这种情况&#xff1f; 使用 LoRA 微调后的模型&#xff0c;在短文本任务上表现完美。 一旦输入序列长度超过 4096 token&#xff0c;效果急剧下降。 困惑矩阵变得模糊…

作者头像 李华
网站建设 2026/6/7 15:48:59

双面氧化应激:既是屏障,也是癌症转移推手

一、引言全球超90%恶性肿瘤相关死亡事件由肿瘤远处转移引发&#xff0c;肿瘤转移是多步骤级联的复杂生物学过程&#xff0c;依次经历原发灶肿瘤细胞脱离、局部侵袭、血管内渗、血液循环存活、血管外渗、远端器官定植增殖六大关键环节。癌细胞在从原发灶向远端脏器迁徙全过程中&…

作者头像 李华