当前位置: 首页 > news >正文

c++结构体讲解

 C++ 中,结构体(struct) 是一种用户自定义的数据类型,用于将不同类型的数据(变量)组合在一起,形成一个逻辑整体,方便管理和操作相关联的数据。结构体可以理解为 “数据的集合”,尤其适合表示具有多个属性的实体(如学生、坐标点、汽车等)。

结构体的核心特点:

  1. 组合多种数据类型:结构体中可以包含不同类型的成员(如intstringdouble等),甚至可以包含其他结构体。
  2. 支持成员函数:C++ 中的结构体不仅可以有成员变量,还可以有成员函数(用于操作结构体数据),这一点与class类似(主要区别是默认访问权限:struct默认publicclass默认private)。
  3. 自定义类型:定义结构体后,可以像内置类型(如int)一样声明变量、作为函数参数或返回值。

结构体的基本用法

1. 结构体的定义

使用struct关键字定义结构体,语法如下:

struct 结构体名 {// 成员变量(属性)数据类型 成员名1;数据类型 成员名2;...// 成员函数(操作)返回值类型 函数名(参数列表) {// 函数体(操作成员变量)}
};
2. 结构体变量的声明与初始化

定义结构体后,可以声明结构体变量,并对其成员初始化。

3. 访问结构体成员

通过 点运算符(.) 访问结构体变量的成员;如果是结构体指针,则通过 箭头运算符(->) 访问。

代码示例

下面通过 3 个示例逐步展示结构体的用法:

示例 1:基础结构体(仅成员变量)

定义一个表示 “二维坐标点” 的结构体,包含xy坐标,演示变量声明、赋值和访问。

#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 = &rect;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默认publicclass默认private),实际开发中可根据需求选择使用。

示例二详解:

在 void setScore(double newScore) 这个成员函数中,double newScore 是一个函数参数,它的作用和可传入的数据类型可以从两方面理解:

1. 可以传入什么数据?

double 是 C++ 中的双精度浮点数类型,因此 newScore 可以接收以下几类数据:

  • double 类型的字面量(带小数点的数值):比如 95.088.5100.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=10width=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=10width=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=10width=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=20width=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 函数中结构体的引用传递步骤是:绑定原结构体为别名→直接修改原结构体成员→函数结束后原结构体已更新;核心作用是:实现对原结构体的修改,同时避免复制开销,让代码更简洁高效,非常适合需要 “修改输入参数” 的场景。

   

http://www.dtcms.com/a/507047.html

相关文章:

  • 青岛商城网站建设网站相互推广怎么做
  • Linux学习笔记(九)--Linux进程终止与进程等待
  • 虚幻引擎5 GAS开发俯视角RPG游戏 P06-09 玩家等级与战斗接口
  • JavaSE内容梳理与整合
  • JavaScript日期处理:格式化与倒计时实现
  • 网页与网站设计 什么是属性网站开发用的框架
  • 长沙正规网站建设价格公司概况简介
  • STM32卡尔曼滤波算法详解与实战应用
  • 【自适应粒子滤波 代码】Sage Husa自适应粒子滤波,用于克服初始Q和R不准确的问题,一维非线性滤波。附有完整的MATLAB代码
  • 未来的 AI 操作系统(三)——智能的中枢:从模型到系统的统一
  • 群晖无公网IP内网穿透工具—ZeroNews(零讯)套件详解
  • [日常使用]Anaconda 常见问题排查手册
  • 【Python入门】第3篇:流程控制之条件判断
  • 网站建设初级教程seo高效优化
  • 智能排课系统实战 Java+MySQL实现课程自动编排与冲突检测
  • 【EE初阶 - 网络原理】传输层协议
  • 电子商务网站建设的难点设计创意网站推荐
  • 【Linux环境下安装】SpringBoot应用环境安装(五)-milvus安装
  • Windows使用docker安装milvus的配置文件
  • 记录之Ubuntu22.4虚拟机及hadoop为分布式安装
  • K8s 运维三大核心难题:DNS 故障、有状态存储、AI 赋能 SRE 解决方案
  • c#WPF基础知识
  • 云栖实录|阿里云 Milvus:AI 时代的专业级向量数据库
  • 科技网站小编账号运营竞争性谈判
  • 华为FreeBuds 7i空间音频不灵敏怎么办?
  • Java Stream 高级应用:优雅地扁平化(FlatMap)递归树形结构数据
  • git推送本地仓库到远程 以及 模拟多人协作
  • 【开题答辩实录分享】以《预约上门维修服务运营与数据分析系统的设计与实现》为例进行答辩实录分享
  • 数据结构7:栈和队列
  • SpringBoot的启动流程原理——小白的魔法引擎探秘