---
title: "结构与联合体"
description: "从环境准备到 STL，系统学习 C++ 编程的基础知识。"
---

> Documentation Index
> Fetch the complete documentation index at: https://cppguide.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# 结构与联合体

![](https://img.dailyup.xyz/originals/8208463142414-4003a93d-ab89-45d9-aa64-cb3c9a1ba106.png)


在C++中，结构体（struct）、联合体（union）和类（class）都是用于组织数据的复合数据类型。它们允许我们将不同类型的变量组合在一起，从而更有效地管理数据。

## 结构体

结构体（`struct`）是由用户自定义的数据类型，它允许你将 **不同类型** 的数据项 **组合** 在一起，形成一个有机的整体，用来描述一个复杂的实体。

想象一下，如果你要描述一个“学生”实体。一个学生有很多属性：姓名、学号、年龄、成绩等等。这些属性是相关的，它们共同描述了一个学生。如果用单独的变量来存储这些属性，会非常零散，难以管理。

这时我们就可以用结构体来描述“学生”这个实体。
### 定义结构体

我们可以使用 `struct` 关键字来定义结构体：

```cpp
struct 结构体类型名称 {
  // 结构体成员
  数据类型1 成员变量名1;
  数据类型2 成员变量名2;
  ...
};
```

> 需要注意的是，结构体定义的末尾有一个分号 `;`，别漏掉了。

例如，我们要描述“学生”这个实体，可以定义下面这样一个结构体：

```cpp
// 定义一个名为 Student 的结构体
struct Student {
  // 结构体成员
  int id; // 学号
  string name; // 姓名
  int age; // 年龄
  char gender; // 性别
};
```

### 使用结构体

现在有了一个名为 `Student` 的结构体类型，我们就可以像使用基本数据类型（如 `int`）一样，声明结构体类型的变量。

```cpp
struct Student stu1; // 定义学生1
stu1.id = 202310001;
stu1.name = "Jordan";
stu1.age = 23;
stu1.gender = 'Male';
```

下面是完整的代码：

```cpp
#include <iostream>
using namespace std;

// 结构体类型声明
struct Student {
  int id; // 学号
  string name; // 姓名
  int age; // 年龄
  char gender; // 性别
};

int main() {
  struct Student s1; // 定义学生对象1
  s1.id = 202310001;
  s1.name = "Jordan";
  s1.age = 23;
  s1.gender = 'M';
  
  struct Student s2; // 定义学生对象2
  s2.id = 202310002;
  s2.name = "Alice";
  s2.age = 26;
  s2.gender = 'F';
  
  cout << "学生1: " << s1.name << " " << s1.age << " " << s1.gender << endl;
  cout << "学生2: " << s2.name << " " << s2.age << " " << s2.gender << endl;
  
  return 0;
}
```

运行程序，结果如下：

```
学生1: Jordan 23 M
学生2: Alice 26 F
```

上面是先定义结构体（`Student`），再单独创建具体的对象（`s1` 和 `s2`）；也可以直接紧跟在结构体的类型声明之后创建对象，代码如下所示：

```cpp
// 结构体类型声明与创建
struct Student {
  int id; // 学号
  string name; // 姓名
  int age; // 年龄
  char gender; // 性别
} s1, s2, s[100];
```

上面代码在声明结构体类型的同时，创建了三个具体的结构体对象：

- s1，学生对象1
- s2，学生对象2
- s[100]，含有 100 个 `Student` 对象的数组 s

对于结构体类型的数组，访问方式与普通的数组差不多，只是通过数组直接拿到的数组元素是结构体，还需要多一层结构才能访问到具体的数据项。

下面是一个简单的示例，演示了**结构体数组**的访问。

```cpp
#include <iostream>
using namespace std;

struct Student {
  int id; // 学号
  string name; // 姓名
  int age; // 年龄
  char gender; // 性别
} s[100];

int main() {
  int n;
  cin >> n;
  // 生成 n 个 Student 并赋值
  for (int i = 0; i < n; i++) {
    s[i].id = i + 1;
    s[i].name = "name" + to_string(i + 1);
    s[i].age = 10;
    if (i % 2 == 0) s[i].gender = 'M';
    else s[i].gender = 'F';
  }
  
  for (int i = 0; i < n; i++) {
    cout << s[i].id << " " << s[i].name << " " << s[i].age <<  " " << s[i].gender << endl;
  }
  
  return 0;
}
```

当输入 $n = 5$ 时，程序运行结果如下：

```
1 name1 10 M
2 name2 10 F
3 name3 10 M
4 name4 10 F
5 name5 10 M
```

### 结构体指针

我们也可以使用结构体指针来访问结构体的元素，有下面两种方式定义结构体指针。

**定义结构体指针**

你可以用一个结构体对象的地址来创建一个指针变量（也就是结构体指针变量）。

```cpp
Student s;
s.id = 1;
s.name = "Jordan";
s.age = 23;
s.gender = 'M';

Student* p = &s; // 定义了一个指向 Student 结构的指针
```

**使用指针访问结构体成员**

通过指针可以访问结构体的成员，有两种常见的方式：

1. 通过解引用操作符（`*`） 访问

通过解引用操作符*，我们可以访问指针所指向的结构体实例，并使用点操作符.访问其成员：

```cpp
Student* p = &s; // 定义了一个指向 Student 结构的指针

cout << "Id: " << (*p).id << ", Name: " << (*p).name << endl;
```

2. 通过箭头操作符（`->`）访问

C++提供了一种更简便的方式，即使用箭头操作符 `->` 来访问成员，它结合了解引用和访问成员的操作：

```cpp
Student* p = &s; // 定义了一个指向 Student 结构的指针

cout << "Id: " << p->id << ", Name: " << p->name << endl;
```

## 联合体

联合体（union）也是一种将多个不同类型的数据组合在一起的用户定义的数据类型，但与结构体不同的是，**联合体的所有成员共享相同的内存空间**。这意味着在**同一时间只能有一个成员占用存储空间**（存储有效值）。

### 定义联合体

使用 `union` 关键字来定义联合体，语法如下：

```cpp
// 定义一个名为 Data 的联合体
union Data {
  // 联合体成员
  int intValue;
  float floatValue;
  char charValue;
};

```

### 使用联合体

我们可以通过以下方式创建和使用联合体：

```cpp
// 声明一个 Data 类型的联合体变量
Data data;

data.intValue = 10;
cout << data.intValue << endl;

data.floatValue = 3.14;
cout << data.floatValue << endl;
```

下面是一个完整的示例，展示了如何定义和使用联合体：

```cpp
#include <iostream>
using namespace std;

union Data {
  int intValue;
  float floatValue;
  char charValue;
};

int main() {
  Data data;
  data.intValue = 10;
  cout << data.intValue << endl;

  data.floatValue = 3.14;
  cout << data.floatValue << endl;
  
  // 如果这时再去访问之前的 intValue，结果是不可预测的！！！
  cout << data.intValue << endl;

  data.charValue = 'A';
  cout << data.charValue << endl;

  return 0;
}
```

结构体与联合体看起来很相似，但它们在内存使用和应用场景上有着显著的不同。

- **内存分配**：结构体的每个成员都有自己独立的内存空间，而联合体的所有成员共享同一块内存。
- **使用场景**：结构体适合用于需要同时存储和访问多个不同类型的数据的情况；联合体适合用于节省内存的场景，尤其是在同一时间只需要一个数据成员的情况下。

> **重要：** 使用联合体时，你需要自己负责跟踪当前联合体中存储的是哪个成员的值。如果你向一个成员赋值后，又去读取另一个成员的值，这是**未定义行为 (Undefined Behavior)**，结果是不可预测的。

## 类

类是 C++ 中 **面向对象编程 (Object-Oriented Programming, OOP)** 的核心概念。它比结构体更强大，不仅可以组合数据，还可以定义操作这些数据的**行为**。

你可以把类看作一个更完善的“模板”或“蓝图”，它不仅描述了对象的**属性**（数据成员），还描述了对象能做什么（**成员函数**或方法）。

### 如何定义类

使用 `class` 关键字来定义类，下面是一个完整的示例代码：

```cpp
#include <bits/stdc++.h>
using namespace std;

// 定义一个名为 Dog 的类
class Dog {
private: // 私有成员，只能在类内部访问
  string name;
  int age;

public: // 公有成员，可以在类外部访问
  // 成员函数 (方法)
  // 构造函数 (用于创建对象时初始化成员)
  Dog(string n, int a) {
	name = n;
	age = a;
	cout << "一只名为 " << name << " 的狗被创建了。" << endl;
  }

  // 成员函数：获取名字
  string getName() {
	return name;
  }

  // 成员函数：获取年龄
  int getAge() {
        return age;
  }

  // 成员函数：执行行为
  void bark() {
    cout << name << " 汪汪叫！" << endl;
  }

  // 析构函数 (在对象销毁时执行)
  ~Dog() {
    cout << "一只名为 " << name << " 的狗被销毁了。" << endl;
  }
}; // 注意类定义结束后的分号 ;

int main() {
  // 这里是使用类的地方，稍后会讲
  return 0;
}

```

下面是关于这段代码的解释：
- `class Dog`：定义了一个名为 `Dog` 的类类型。
- 类可以包含**数据成员**（如 `name`, `age`）和**成员函数**（如 `Dog()`, `getName()`, `bark()`, `~Dog()`）。
- **访问修饰符 (`public`, `private`, `protected`)：** 用来控制类成员的访问权限。
    - `public`：公有成员，可以在类的内部和外部任何地方访问。
    - `private`：私有成员，只能在类的**内部**访问。这是实现**封装 (Encapsulation)** 的重要手段，可以保护数据不被随意修改。
    - `protected`：保护成员，可以在类的内部和其派生类（继承类）中访问。
- **构造函数：** 与类名同名，没有返回类型。在创建类的对象时自动调用，用于初始化对象的状态。
- **析构函数：** 函数名前面加 `~`，与类名同名，没有返回类型和参数。在对象生命周期结束时自动调用，用于清理资源。

### 如何使用类

定义了类类型后，你就可以创建类的**对象 (Object)**，也称为**实例化 (Instantiation)**。对象是类的具体实例。

```cpp
#include <bits/stdc++.h>
using namespace std;

class Dog {
private:
  string name;
  int age;
public:
  Dog(string n, int a) { // 构造函数
    name = n;
    age = a;
    cout << "一只名为 " << name << " 的狗被创建了。" << endl;
  }
  string getName() { return name; }
  int getAge() { return age; }
  void bark() { cout << name << " 汪汪叫！" << endl; }
  ~Dog() { cout << "一只名为 " << name << " 的狗被销毁了。" << endl; } // 析构函数
};

int main() {
  // 创建一个 Dog 类的对象
  // 调用了构造函数 Dog("旺财", 3)
  Dog myDog("旺财", 3);

  // 访问公有成员函数：使用点运算符 '.'
  cout << "我的狗叫: " << myDog.getName() << endl;
  cout << "它的年龄是: " << myDog.getAge() << endl;
  myDog.bark(); // 调用 bark 方法

  // 尝试访问私有成员 (会编译错误)
  // cout << myDog.age << endl; // 错误！age 是 private 的

  // 对象在 main 函数结束时会被销毁，自动调用析构函数 ~Dog()

  return 0;
}

```

## 小结

- **结构体 (struct)：** 主要用于将不同类型的数据打包成一个单一的单元。在 C++ 中，它也可以包含成员函数，但默认成员是 `public`的，常用于表示纯粹的数据结构。
- **联合体 (union)：** 是一种特殊的结构，其成员共享同一块内存空间，用于在不同类型的数据之间节省内存。使用时需要手动管理当前哪个成员是有效的。
- **类 (class)：** 是面向对象编程的基础，将数据和操作数据的方法封装在一起。通过访问修饰符实现封装，默认成员是 `private` 的，更强调数据保护和行为定义。

在实际编程中：
- 如果你只是想简单地组合一些数据，并且希望这些数据可以方便地在外部直接访问，可以使用**结构体**。
- 如果你需要在不同时间存储不同类型的数据，并且希望最大程度地节省内存，可以考虑使用**联合体**（但要注意使用时的安全性）。
- 如果你想定义一个具有特定属性和行为的对象类型，实现封装、继承、多态等面向对象特性，那么应该使用**类**。

在 C++ 中，`struct` 和 `class` 在功能上非常相似，主要区别在于默认访问权限。习惯上，`struct` 更常用于那些主要由数据组成的类型（Plain Old Data, POD），而 `class` 更常用于那些包含复杂行为和需要封装的类型。

Source: https://cppguide.xyz/basic/struct/index.mdx
