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.0
    
    return 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,8
    cout << "修改后面积:" << getArea(rect) << endl;  // 20*8=160
    
    return 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 函数中结构体的引用传递步骤是:绑定原结构体为别名→直接修改原结构体成员→函数结束后原结构体已更新;核心作用是:实现对原结构体的修改,同时避免复制开销,让代码更简洁高效,非常适合需要 “修改输入参数” 的场景。

   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值