c++结构体讲解
C++ 中,结构体(struct) 是一种用户自定义的数据类型,用于将不同类型的数据(变量)组合在一起,形成一个逻辑整体,方便管理和操作相关联的数据。结构体可以理解为 “数据的集合”,尤其适合表示具有多个属性的实体(如学生、坐标点、汽车等)。
结构体的核心特点:
- 组合多种数据类型:结构体中可以包含不同类型的成员(如
int
、string
、double
等),甚至可以包含其他结构体。 - 支持成员函数:C++ 中的结构体不仅可以有成员变量,还可以有成员函数(用于操作结构体数据),这一点与
class
类似(主要区别是默认访问权限:struct
默认public
,class
默认private
)。 - 自定义类型:定义结构体后,可以像内置类型(如
int
)一样声明变量、作为函数参数或返回值。
结构体的基本用法
1. 结构体的定义
使用struct
关键字定义结构体,语法如下:
struct 结构体名 {// 成员变量(属性)数据类型 成员名1;数据类型 成员名2;...// 成员函数(操作)返回值类型 函数名(参数列表) {// 函数体(操作成员变量)}
};
2. 结构体变量的声明与初始化
定义结构体后,可以声明结构体变量,并对其成员初始化。
3. 访问结构体成员
通过 点运算符(.) 访问结构体变量的成员;如果是结构体指针,则通过 箭头运算符(->) 访问。
代码示例
下面通过 3 个示例逐步展示结构体的用法:
示例 1:基础结构体(仅成员变量)
定义一个表示 “二维坐标点” 的结构体,包含x
和y
坐标,演示变量声明、赋值和访问。
#include <iostream>
using namespace std;// 定义结构体:表示二维坐标点
struct Point {int x; // x坐标int y; // y坐标
};int main() {// 声明结构体变量并初始化(方式1:定义时初始化)Point p1 = {3, 4}; // p1的x=3,y=4// 声明结构体变量后单独赋值(方式2:逐个成员赋值)Point p2;p2.x = 5; // 用.访问成员变量p2.y = 6;// 输出坐标cout << "p1坐标:(" << p1.x << ", " << p1.y << ")" << endl; // 输出:(3, 4)cout << "p2坐标:(" << p2.x << ", " << p2.y << ")" << endl; // 输出:(5, 6)return 0;
}
示例 2:带成员函数的结构体
定义一个 “学生” 结构体,包含姓名、年龄、成绩等成员变量,以及用于显示信息和修改成绩的成员函数。
#include <iostream>
#include <string> // 需包含string头文件
using namespace std;// 定义结构体:表示学生
struct Student {// 成员变量(属性)string name; // 姓名int age; // 年龄double score; // 成绩// 成员函数(操作):显示学生信息void showInfo() {cout << "姓名:" << name << endl;cout << "年龄:" << age << endl;cout << "成绩:" << score << endl;}// 成员函数:修改成绩void setScore(double newScore) {score = newScore; // 直接操作成员变量}
};int main() {// 声明学生变量并初始化Student s1 = {"张三", 18, 90.5};// 调用成员函数显示信息cout << "修改前的信息:" << endl;s1.showInfo(); // 输出:姓名:张三 年龄:18 成绩:90.5// 调用成员函数修改成绩s1.setScore(95.0);// 再次显示信息cout << "\n修改后的信息:" << endl;s1.showInfo(); // 输出:姓名:张三 年龄:18 成绩:95.0return 0;
}
示例 3:结构体指针与函数参数
演示结构体指针的使用,以及结构体作为函数参数(值传递 / 引用传递)。
#include <iostream>
using namespace std;// 定义结构体:表示矩形
struct Rectangle {int length; // 长度int width; // 宽度
};// 函数:计算矩形面积(结构体值传递)
int getArea(Rectangle rect) {return rect.length * rect.width;
}// 函数:修改矩形尺寸(结构体引用传递,直接修改原变量)
void resize(Rectangle& rect, int newLen, int newWid) {rect.length = newLen;rect.width = newWid;
}int main() {// 声明矩形变量Rectangle rect = {10, 5}; // 长10,宽5// 结构体指针(用->访问成员)Rectangle* pRect = ▭cout << "初始长度:" << pRect->length << ",初始宽度:" << pRect->width << endl; // 10,5// 计算面积cout << "初始面积:" << getArea(rect) << endl; // 10*5=50// 修改尺寸(引用传递)resize(rect, 20, 8);cout << "修改后长度:" << rect.length << ",修改后宽度:" << rect.width << endl; // 20,8cout << "修改后面积:" << getArea(rect) << endl; // 20*8=160return 0;
}
总结
结构体是 C++ 中组织关联数据的重要工具,通过将变量和操作函数封装在一起,使代码更清晰、逻辑更紧凑。它与类(class
)的核心功能类似,主要差异在于默认访问权限(struct
默认public
,class
默认private
),实际开发中可根据需求选择使用。
示例二详解:
在 void setScore(double newScore)
这个成员函数中,double newScore
是一个函数参数,它的作用和可传入的数据类型可以从两方面理解:
1. 可以传入什么数据?
double
是 C++ 中的双精度浮点数类型,因此 newScore
可以接收以下几类数据:
double
类型的字面量(带小数点的数值):比如95.0
、88.5
、100.0
等(示例中传入的95.0
就是这类)。int
类型的整数:C++ 会自动进行隐式类型转换,将整数转换为对应的double
类型。例如传入90
,会被自动转为90.0
。其他可转换为
double
的类型:比如float
类型(单精度浮点数),也会被隐式转换为double
后传入(但double
精度更高,更适合表示成绩这类可能带小数的数据)。
2. 这个参数的作用是什么?
newScore
的核心作用是传递一个 “新的成绩值”,供 setScore
函数使用,最终目的是更新结构体 Student
中的 score
成员变量。
具体来说:
当调用
s1.setScore(95.0)
时,95.0
会被传递给newScore
,函数内部通过score = newScore
将s1
的score
从原来的90.5
改成95.0
。这种通过函数修改成员变量的方式,体现了 “封装” 的思想(即使当前
score
是public
的,后续如果将score
改为private
,仍可通过setScore
安全修改,避免直接暴露成员变量)。
总结
double newScore
是一个接收 “新成绩” 的参数,可传入小数(double
)、整数(int
等可转换类型),作用是将新的成绩值传递给函数,用于更新学生对象的成绩。
示例三详解:
在函数 int getArea(Rectangle rect)
中,结构体 Rectangle
作为参数的传递方式是值传递(pass-by-value)。这种传递方式的核心是 “复制原结构体的数据,函数内部操作副本,不影响原结构体”。下面详细拆解其传递步骤和作用:
一、结构体值传递的详细步骤
假设在 main
函数中调用 getArea(rect)
(其中 rect
是已初始化的 Rectangle
变量,length=10
,width=5
),整个传递和执行过程如下:
步骤 1:调用函数时,创建结构体参数的 “副本”
当执行 getArea(rect)
时,编译器会为函数参数 rect
(形参)创建一个全新的、与实参 rect
完全相同的副本。
- 复制规则:对结构体的每个成员变量逐一复制(即 “浅拷贝”)。这里会将实参
rect
的length
(10)复制到形参rect
的length
,将实参的width
(5)复制到形参的width
。 - 此时内存中存在两个独立的
Rectangle
变量:原变量rect
(在main
函数中)和形参副本rect
(在getArea
函数的栈空间中)。
步骤 2:函数内部使用副本进行计算
getArea
函数内部的逻辑 return rect.length * rect.width
,实际操作的是步骤 1 中创建的副本:
- 副本的
length=10
,width=5
,因此计算结果为10*5=50
。
步骤 3:函数执行结束,副本被销毁
getArea
函数返回结果(50)后,其栈空间中的形参副本(结构体 rect
)会被自动释放(内存回收),不会残留。
关键结论:
整个过程中,原结构体变量 rect
(在 main
中)没有任何改变,因为函数操作的是它的副本。
二、结构体值传递的作用
这种传递方式的设计目的和适用场景如下:
1. 保护原数据不被修改
值传递中,函数内部对形参(副本)的任何修改都不会影响实参(原结构体)。
- 例如,即使在
getArea
中添加rect.length = 100
,原main
函数中的rect.length
仍然是 10(因为修改的是副本)。 - 这对
getArea
这类 “只读” 函数(仅需使用结构体数据,无需修改)非常重要,能避免原数据被意外篡改。
2. 适合 “小结构体” 的传递
当结构体成员较少(如 Rectangle
只有两个 int
成员)时,复制副本的开销(内存和时间)很小,值传递高效且直观。
3. 符合 “函数独立性” 原则
函数的计算结果仅依赖于输入的参数(副本),不依赖外部变量的状态,也不影响外部变量,使函数更独立、易维护。
在函数 void resize(Rectangle& rect, int newLen, int newWid)
中,结构体 Rectangle
作为参数的传递方式是引用传递(pass-by-reference)。引用本质是 “变量的别名”,因此引用传递的核心是 “直接操作原结构体变量,不创建副本”。下面详细说明其传递步骤和作用:
一、结构体引用传递的详细步骤
假设在 main
函数中调用 resize(rect, 20, 8)
(其中 rect
是原结构体变量,初始 length=10
,width=5
),整个过程如下:
步骤 1:函数调用时,为原结构体创建 “别名”
当执行 resize(rect, 20, 8)
时,函数的第一个参数 Rectangle& rect
(形参)会成为实参 rect
(原结构体变量)的别名。
- 这里的 “引用”(
&
)表示形参rect
不是新的变量,而是直接绑定到实参rect
上,两者共享同一块内存空间。 - 此时内存中不会创建任何副本,形参和实参指向同一个
Rectangle
结构体(地址相同)。
步骤 2:函数内部直接修改原结构体的成员
函数内部的代码 rect.length = newLen; rect.width = newWid;
看似操作的是形参 rect
,但由于形参是原变量的别名,实际修改的是原结构体 rect
的成员:
newLen=20
被赋值给rect.length
,原结构体的长度从 10 变为 20;newWid=8
被赋值给rect.width
,原结构体的宽度从 5 变为 8。
步骤 3:函数执行结束,引用失效但原结构体已被修改
resize
函数执行完毕后,形参的引用(别名)会被销毁(不再作为原变量的别名),但原结构体 rect
已经被永久修改(length=20
,width=8
)。
二、结构体引用传递的作用
引用传递的设计完全是为了满足 “需要修改原结构体” 的场景,具体作用如下:
1. 直接修改原结构体变量
这是引用传递最核心的作用。resize
函数的目的是 “调整原矩形的尺寸”,而引用传递允许函数直接操作原变量,无需通过指针间接访问,修改结果能直接反映到原结构体上。
- 如果改用值传递(
void resize(Rectangle rect, ...)
),函数内部修改的只是副本,原结构体不会有任何变化,无法实现 “调整尺寸” 的功能。
2. 避免结构体复制的开销
当结构体成员较多或包含大型数据(如长字符串、数组等)时,值传递会复制整个结构体(逐个成员复制),导致额外的内存占用和时间消耗。
- 引用传递无需复制,直接绑定原变量,能显著提高效率(尤其对大型结构体)。
- 示例中的
Rectangle
只有两个int
成员,复制开销很小,但换成包含 100 个成员的结构体时,引用传递的优势会非常明显。
3. 代码更简洁、易读
相比指针传递(如 void resize(Rectangle* rect, ...)
),引用传递无需使用 *
解引用或 ->
访问成员,语法更接近直接操作变量,代码更直观。
- 例如:引用传递用
rect.length
访问成员,而指针传递需要rect->length
,引用的写法更简洁。
4. 保证参数的 “可读性”(非空性)
引用必须绑定到一个已存在的变量(不能为 nullptr
),而指针可能为空(nullptr
)。因此使用引用传递可以避免函数内部判断指针是否为空的额外逻辑,减少出错风险。
对比:引用传递与值传递的核心差异
传递方式 | 传递方式 | 能否修改原变量 | 适用场景 |
值传递 | 是(复制实参) | 不能 | 仅需读取数据,不修改原变量(如 getArea ) |
引用传递 | 否(绑定原变量) | 能 | 需要修改原变量(如 resize ) |
总结
resize
函数中结构体的引用传递步骤是:绑定原结构体为别名→直接修改原结构体成员→函数结束后原结构体已更新;核心作用是:实现对原结构体的修改,同时避免复制开销,让代码更简洁高效,非常适合需要 “修改输入参数” 的场景。