C++ 结构体(struct):自定义数据类型的核心解析

C++ 结构体(struct)

  • 一、结构体的核心背景与用途
      • [// 糟糕的方式:变量分散,无关联性](#// 糟糕的方式:变量分散,无关联性)
    • [1.2 结构体与其他类型的区别](#1.2 结构体与其他类型的区别)
  • 二、结构体的基础用法
    • [2.1 结构体的定义与声明](#2.1 结构体的定义与声明)
    • [2.2 结构体变量的创建与初始化](#2.2 结构体变量的创建与初始化)
      • (1)默认初始化(成员变量为默认值)
      • [(2)聚合初始化(C++11 前 / 后)](#(2)聚合初始化(C++11 前 / 后))
        • [// 方式1:按成员顺序初始化(C++11 前)](#// 方式1:按成员顺序初始化(C++11 前))
        • [// 方式2:列表初始化(C++11,推荐,可省略等号)](#// 方式2:列表初始化(C++11,推荐,可省略等号))
        • [// 方式3:部分初始化(未指定的成员为默认值)](#// 方式3:部分初始化(未指定的成员为默认值))
      • (3)指针方式创建
    • [2.3 结构体的访问与使用](#2.3 结构体的访问与使用)
  • 三、结构体的内存布局
    • [3.1 基本内存布局(无对齐)](#3.1 基本内存布局(无对齐))
    • [3.2 内存对齐(核心!)](#3.2 内存对齐(核心!))
    • [3.3 手动控制对齐(#pragma pack)](#pragma pack))
  • [四、结构体的高级特性(C++ 扩展)](#四、结构体的高级特性(C++ 扩展))
    • [4.1 结构体中定义成员函数](#4.1 结构体中定义成员函数)
    • [4.2 构造函数与默认构造函数](#4.2 构造函数与默认构造函数)
    • [4.3 结构体的继承](#4.3 结构体的继承)
    • [4.4 结构体与 STL 容器](#4.4 结构体与 STL 容器)
  • 五、结构体的常见应用场景
    • [5.1 封装实体数据(最常用)](#5.1 封装实体数据(最常用))
    • [5.2 函数参数 / 返回值](#5.2 函数参数 / 返回值)
    • [5.3 联合使用(union)与位域](#5.3 联合使用(union)与位域)
    • [5.4 与指针 / 数组结合](#5.4 与指针 / 数组结合)
  • 六、常见错误与最佳实践
    • [6.1 常见错误](#6.1 常见错误)
    • [6.2 最佳实践](#6.2 最佳实践)
  • 七、结构体与类的对比
  • 八、总结

结构体(struct)是 C++ 中用于封装不同类型数据的自定义复合类型,是面向过程编程中组织数据的核心工具,也是 C++ 面向对象特性的基础(可包含成员函数、继承等)。本文将从结构体的基础定义、内存布局、核心特性到高级用法,全面解析 C++ 结构体的设计与实战技巧。

一、结构体的核心背景与用途

1.1 问题引入:单一类型的局限性

C++ 内置类型(int、char、double 等)仅能表示单一数据,而实际场景中常需将关联的不同类型数据组合(如学生信息包含姓名、年龄、成绩)。

例如:若用单独变量存储学生信息,代码冗余且关联性差:

cpp

运行

// 糟糕的方式:变量分散,无关联性

string stu_name = "Alice";

int stu_age = 18;

double stu_score = 92.5;

结构体的核心价值:将关联数据封装为一个整体,提升代码的可读性、可维护性。

1.2 结构体与其他类型的区别

类型 核心特性 适用场景

结构体(struct) 可包含不同类型数据 / 成员函数,默认访问权限 public 封装关联数据(如实体信息)

类(class) 与结构体几乎一致,默认访问权限 private 面向对象编程(封装 / 继承)

数组 仅存储相同类型数据,固定大小 同类型数据的有序集合

二、结构体的基础用法

2.1 结构体的定义与声明

语法:

cpp

运行

// 定义结构体类型(自定义类型)

struct 结构体名 {

// 成员变量(数据成员):类型 + 名称

类型1 成员名1;

类型2 成员名2;

// ... 更多成员

}; // 注意末尾分号!

示例:定义学生结构体

cpp

运行

#include

#include

using namespace std;

// 定义结构体类型:Student(自定义类型)

struct Student {

// 成员变量(不同类型)

string name; // 姓名

int age; // 年龄

double score; // 成绩

}; // 分号不可省略

2.2 结构体变量的创建与初始化

结构体变量的创建有多种方式,核心是为成员变量赋值:

(1)默认初始化(成员变量为默认值)

cpp

运行

// 创建结构体变量(默认初始化:string为空串,int/double为随机值)

Student stu1;

// 赋值:通过 . 访问成员变量

stu1.name = "Bob";

stu1.age = 19;

stu1.score = 88.0;

(2)聚合初始化(C++11 前 / 后)

cpp

运行

// 方式1:按成员顺序初始化(C++11 前)

Student stu2 = {"Alice", 18, 92.5};

// 方式2:列表初始化(C++11,推荐,可省略等号)

Student stu3{"Charlie", 20, 79.5};

// 方式3:部分初始化(未指定的成员为默认值)

Student stu4 = {"David"}; // name="David",age=0,score=0.0

(3)指针方式创建

cpp

运行

// 动态分配结构体(堆内存)

Student* stu_ptr = new Student{"Ella", 17, 95.0};

// 指针访问成员:-> 运算符

cout << stu_ptr->name << endl; // Ella

cout << stu_ptr->age << endl; // 17

// 释放堆内存

delete stu_ptr;

stu_ptr = nullptr;

2.3 结构体的访问与使用

普通变量:通过 . 运算符访问成员。

指针变量:通过 -> 运算符访问成员(等价于 (*指针).成员)。

完整示例:

cpp

运行

int main() {

Student stu = {"Alice", 18, 92.5};

复制代码
// 访问成员
cout << "姓名:" << stu.name << endl;
cout << "年龄:" << stu.age << endl;
cout << "成绩:" << stu.score << endl;

// 修改成员
stu.score = 94.0;
cout << "修改后成绩:" << stu.score << endl;

// 指针访问
Student* p = &stu;
cout << "指针访问姓名:" << p->name << endl;  // 等价于 (*p).name
return 0;

}

三、结构体的内存布局

结构体的成员在内存中连续存储,但可能因 内存对齐 产生 "空洞"(padding),这是理解结构体内存占用的关键。

3.1 基本内存布局(无对齐)

若成员类型的大小为 1/2/4/8 字节,且无对齐优化,内存连续:

cpp

运行

// 示例:无对齐的内存布局(假设地址从 0x1000 开始)

struct Test {

char a; // 1字节:0x1000

int b; // 4字节:0x1001 ~ 0x1004

double c; // 8字节:0x1005 ~ 0x100C

};

3.2 内存对齐(核心!)

为提升 CPU 访问效率,编译器会自动对结构体成员进行内存对齐(默认按 "最大成员大小" 对齐)。

示例:对齐后的内存布局

cpp

运行

struct Test {

char a; // 1字节:0x1000,后面填充3字节(0x1001~0x1003)

int b; // 4字节:0x1004 ~ 0x1007

double c; // 8字节:0x1008 ~ 0x100F

};

// 总大小:1 + 3(padding) + 4 + 8 = 16 字节(而非 13 字节)

验证内存大小:

cpp

运行

cout << sizeof(Test) << endl; // 输出 16(而非 1+4+8=13)

3.3 手动控制对齐(#pragma pack)

可通过编译器指令 #pragma pack 强制指定对齐大小:

cpp

运行

#pragma pack(1) // 按1字节对齐(取消填充)

struct Test {

char a;

int b;

double c;

};

#pragma pack() // 恢复默认对齐

cout << sizeof(Test) << endl; // 输出 13(1+4+8)

注意:手动取消对齐可能降低访问效率,仅在需要紧凑存储(如网络传输、文件存储)时使用。

四、结构体的高级特性(C++ 扩展)

C++ 对 C 语言的结构体进行了大幅扩展,使其支持成员函数、构造函数、继承等面向对象特性。

4.1 结构体中定义成员函数

结构体可包含成员函数(包括普通函数、构造函数、析构函数),行为与类完全一致:

cpp

运行

struct Student {

// 成员变量

string name;

int age;

double score;

复制代码
// 普通成员函数:打印信息
void printInfo() {
    cout << "姓名:" << name << ",年龄:" << age << ",成绩:" << score << endl;
}

// 构造函数:初始化成员(替代手动赋值)
Student(string n, int a, double s) : name(n), age(a), score(s) {}

// 析构函数(可选,用于释放资源)
~Student() {}

};

// 使用构造函数创建对象

int main() {

Student stu("Alice", 18, 92.5);

stu.printInfo(); // 调用成员函数

return 0;

}

4.2 构造函数与默认构造函数

构造函数:与结构体同名,无返回值,用于初始化成员变量。

默认构造函数:若未自定义构造函数,编译器会自动生成(无参数,成员变量默认初始化);若自定义构造函数,默认构造函数会被覆盖(需手动声明)。

cpp

运行

struct Student {

string name;

int age;

复制代码
// 自定义构造函数
Student(string n) : name(n) {}

// 手动声明默认构造函数(C++11)
Student() = default;

};

// 合法:调用默认构造函数

Student stu1;

// 合法:调用自定义构造函数

Student stu2("Bob");

4.3 结构体的继承

C++ 结构体支持继承(默认 public 继承,区别于类的 private 继承):

cpp

运行

// 基结构体

struct Person {

string name;

int age;

};

// 派生结构体:继承 Person 的成员

struct Student : Person {

double score; // 新增成员

复制代码
// 构造函数:调用基结构体构造函数
Student(string n, int a, double s) : Person{n, a}, score(s) {}

};

int main() {

Student stu("Charlie", 20, 85.0);

cout << stu.name << endl; // 继承的成员

cout << stu.score << endl; // 新增成员

return 0;

}

4.4 结构体与 STL 容器

结构体可作为 STL 容器的元素(需满足可拷贝 / 可赋值):

cpp

运行

#include

int main() {

// 存储结构体的vector

vector stu_list;

stu_list.emplace_back("Alice", 18, 92.5); // 直接构造

stu_list.emplace_back("Bob", 19, 88.0);

复制代码
// 遍历容器
for (const auto& stu : stu_list) {
    stu.printInfo();
}
return 0;

}

五、结构体的常见应用场景

5.1 封装实体数据(最常用)

用于表示具有多个属性的实体(如学生、商品、坐标):

cpp

运行

// 表示二维坐标

struct Point {

int x;

int y;

复制代码
// 成员函数:计算距离原点的平方
int distanceSq() {
    return x*x + y*y;
}

};

5.2 函数参数 / 返回值

将多个关联数据打包为结构体,简化函数参数 / 返回值:

cpp

运行

// 函数返回多个值(通过结构体)

struct Result {

int sum;

int max_val;

int min_val;

};

Result calcArray(const vector& arr) {

Result res{0, arr[0], arr[0]};

for (int num : arr) {

res.sum += num;

res.max_val = max(res.max_val, num);

res.min_val = min(res.min_val, num);

}

return res;

}

5.3 联合使用(union)与位域

结构体可包含 union(共用体)或位域,用于紧凑存储:

cpp

运行

// 位域:用二进制位存储数据(节省空间)

struct Flags {

unsigned int is_valid : 1; // 1位:是否有效

unsigned int is_admin : 1; // 1位:是否管理员

unsigned int level : 3; // 3位:等级(0~7)

};

int main() {

Flags f;

f.is_valid = 1;

f.is_admin = 0;

f.level = 5;

cout << sizeof(f) << endl; // 输出 4(int 大小)

return 0;

}

5.4 与指针 / 数组结合

结构体数组常用于批量存储同类型实体:

cpp

运行

// 结构体数组

Student stu_arr[3] = {

{"Alice", 18, 92.5},

{"Bob", 19, 88.0},

{"Charlie", 20, 85.0}

};

// 遍历数组

for (int i = 0; i < 3; ++i) {

stu_arr[i].printInfo();

}

六、常见错误与最佳实践

6.1 常见错误

遗漏末尾分号结构体定义末尾的分号是语法要求,遗漏会导致编译错误:

cpp

运行

struct Test { int a; } // 错误:无分号

struct Test { int a; }; // 正确

内存对齐误解误以为结构体大小等于成员大小之和,忽略编译器的自动填充:

cpp

运行

struct Test { char a; int b; };

cout << sizeof(Test); // 输出 8(而非 5)

指针访问错误对结构体指针误用 . 运算符(应使用 ->):

cpp

运行

Student* p = new Student();

p.name = "Alice"; // 错误!应为 p->name

(*p).name = "Alice"; // 正确(等价于 p->name)

构造函数覆盖默认构造自定义构造函数后,无法直接创建无参对象(需手动声明默认构造):

cpp

运行

struct Student {

Student(string n) : name(n) {}

string name;

};

Student stu; // 错误!无默认构造函数

6.2 最佳实践

命名规范结构体名采用 帕斯卡命名法(首字母大写,如 Student),成员变量采用小驼峰(如 stuName)或下划线(如 stu_name)。

优先使用构造函数初始化避免手动赋值,通过构造函数确保成员变量被正确初始化:

cpp

运行

// 推荐

Student stu("Alice", 18, 92.5);

// 不推荐(易遗漏赋值)

Student stu;

stu.name = "Alice";

const 修饰只读成员函数不修改成员变量的成员函数,应加 const 修饰,提升代码安全性:

cpp

运行

struct Student {

void printInfo() const { // const 成员函数

cout << name << endl; // 仅读取成员,不修改

}

};

避免过大的结构体结构体若包含大量成员(如数十个),可拆分为多个小结构体(按功能分类),提升可读性。

合理控制内存对齐仅在需要紧凑存储时使用 #pragma pack,否则保持默认对齐(兼顾效率)。

七、结构体与类的对比

C++ 中结构体(struct)和类(class)的唯一核心区别是 默认访问权限:

struct:成员默认 public(包括继承)。

class:成员默认 private(包括继承)。

其余特性(构造函数、析构函数、继承、多态等)完全一致。

选择建议:

若仅封装数据(无复杂逻辑),用 struct(更符合直觉)。

若需封装数据 + 逻辑,且强调封装性(隐藏实现),用 class。

八、总结

结构体是 C++ 中封装复合数据的核心工具,其核心特性可总结为:

基础特性:封装不同类型数据,内存连续存储(可能有对齐填充)。

C++ 扩展:支持成员函数、构造函数、继承、const 修饰等面向对象特性。

核心优势:将关联数据整合为整体,提升代码的可读性和可维护性。

掌握结构体的关键:

理解内存布局与对齐规则(避免内存计算错误)。

熟练使用构造函数初始化成员(避免未初始化问题)。

区分结构体与类的细微差异(默认访问权限)。

结构体是连接 C 语言 "面向过程" 和 C++ "面向对象" 的桥梁,是编写高效、清晰代码的基础工具。

相关推荐
ULTRA??1 小时前
C++类型和容器在MoonBit中的对应关系整理
开发语言·c++·rust
李白同学1 小时前
C++:queue、priority_queue的使用和模拟实现
开发语言·c++
楼田莉子1 小时前
Linux学习:基础IO相关学习
linux·开发语言·c++·后端·学习
.小小陈.1 小时前
C++初阶5:string类使用攻略
开发语言·c++·学习·算法
神奇的代码在哪里1 小时前
C++的演进与我的编程学习之旅:从底层基础到AI应用
c++·人工智能·python·学习·程序人生·个人开发
小年糕是糕手1 小时前
【C++】类和对象(六) -- 友元、内部类、匿名对象、对象拷贝时的编译器优化
开发语言·c++·算法·pdf·github·排序算法
大佬,救命!!!1 小时前
C++本地配置OpenCV
开发语言·c++·opencv·学习笔记·环境配置
噜啦噜啦嘞好1 小时前
Linux:线程池
linux·运维·c++
酷酷的佳2 小时前
用C语言写一个可以排序的程序
c++