【C 语言】结构体 ( 结构体深拷贝 )





一、结构体浅拷贝与深拷贝



结构体 中 嵌套了 指针 , 指针指向的内存 , 如果需要 malloc 在堆内存中 分配内存 , 如果在 该类型 结构体变量 之间互相赋值 ,

  • 如果直接赋值 , 就是浅拷贝 ;
  • 如果赋值时 , 重新为 指针变量 在堆内存中重新申请内存 , 拷贝数据 , 就是 深拷贝 ;

浅拷贝 只会 拷贝 指针变量的值 , 不会拷贝 指针变量 指向的 内存空间的 数据 ;





二、结构体深拷贝



结构体深拷贝 : 如果要实现结构体的深拷贝 , 需要在 浅拷贝 的基础上 , 重新为 指针 在堆内存中分配数据 ;

/**
 * @brief copy_student 执行深拷贝操作
 * @param to
 * @param from
 */
void copy_student(Student *to, Student *from)
{
    // 结构体内存拷贝
    // 该拷贝是浅拷贝
    memcpy(to, from, sizeof (Student));

    // 结构体直接赋值 , 与上面的代码作用相同
    // 该拷贝也是浅拷贝
    //*to = *from;


    // 重新为 address 分配内存
    to->address = (char *)malloc(20);

    // 将 from 中的地址字符串数据 拷贝到 to 中
    strcpy(to->address, from->address);
}




三、结构体深拷贝代码示例



代码示例 :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
 * @brief The Student struct
 * 定义 结构体 数据类型 , 同时为该结构体类型声明 别名
 * 可以直接使用 别名 结构体变量名 声明结构体类型变量
 * 不需要在前面添加 struct 关键字
 */
typedef struct Student
{
    // 声明变量时 , 会自动分配这 5 字节内存
    // 赋值时 , 可以直接使用 = 赋值字符串
    char name[5];
    int age;

    // 声明变量时 , 只会为 4 字节指针分配内存
    // 具体的 字符串内存 需要额外使用 malloc 申请内存
    // 赋值时 , 必须使用 strcpy 函数 , 向堆内存赋值
    char *address;
}Student;

/**
 * @brief copy_student 执行深拷贝操作
 * @param to
 * @param from
 */
void copy_student(Student *to, Student *from)
{
    // 结构体内存拷贝
    // 该拷贝是浅拷贝
    memcpy(to, from, sizeof (Student));

    // 结构体直接赋值 , 与上面的代码作用相同
    // 该拷贝也是浅拷贝
    //*to = *from;


    // 重新为 address 分配内存
    to->address = (char *)malloc(20);

    // 将 from 中的地址字符串数据 拷贝到 to 中
    strcpy(to->address, from->address);
}



/**
 * @brief 主函数入口
 * @return
 */
int main(int argc, char* argv[], char**env)
{
    Student s1;
    Student s2;

    // 为 s1.age 赋值
    s1.age = 18;

    // 为 s1.name 赋值
    // 该成员是 数组 , 在 s1 结构体变量声明时 , 就分配好了内存
    strcpy(s1.name, "Tom");

    // 给 s1.address 在堆内存分配内存
    s1.address = (char *)malloc(20);
    strcpy(s1.address, "Beijing");


    // 将 s1 赋值给 s2
    copy_student(&s2, &s1);

    printf("s1 : name = %s, age = %d, address = %s, %d\n", s1.name, s1.age, s1.address, &s1.address);
    printf("s2 : name = %s, age = %d, address = %s, %d\n", s2.name, s2.age, s2.address, &s2.address);

    // 命令行不要退出
    system("pause");
    return 0;
}

执行结果 :

s1 : name = Tom, age = 18, address = Beijing, 6422220
s2 : name = Tom, age = 18, address = Beijing, 6422204
请按任意键继续. . .

在这里插入图片描述

<think>嗯,用户想了解C语言结构体,我需要详细解释清楚。首先,结构体的定义应该怎么讲呢?得从基本概念入手,说明结构体是用户自定义的数据类型,可以组合不同类型的数据。然后举例说明,比如用struct关键字定义,成员变量的声明。 接下来,结构体的声明和初始化部分。用户可能需要知道如何声明结构体变量,有没有不同的声明方式,比如先定义结构体类型再声明变量,或者在定义类型的同时声明变量。初始化的时候,要提到初始化的方法,包括按顺序初始化和指定成员初始化,特别是C99之后的指定初始化器,这样用户知道有更灵活的方式。 然后,结构体成员的访问,这部分很重要。需要用点运算符来访问,举例子说明如何赋值和读取成员的值。可能还需要提到结构体变量作为函数参数传递的情况,说明值传递和指针传递的区别,以及如何通过指针访问成员,也就是箭头运算符->的使用。 结构体的内存对齐问题,这个对于优化和理解结构体大小很关键。需要解释对齐原则,比如成员的对齐数和编译器设置的关系,举例说明如何计算结构体的大小,可能存在内存浪费的情况。可能还需要提到#pragma pack预处理指令,用来调整对齐方式。 结构体的嵌套使用,比如结构体中可以包含其他结构体作为成员,或者包含指向自己类型的指针,这在链表或树结构中很常见。这部分需要举例说明,比如学生结构体里包含生日结构体,或者链表节点的结构体包含next指针。 最后,结构体的应用场景,比如复杂数据建模、文件读写、函数参数传递和数据结构实现。这些实际应用的例子能帮助用户理解结构体的重要性。可能还要提到typedef和结构体结合使用,简化类型名称,这样代码更简洁。 需要检查是否有遗漏的部分,比如结构体与数组的区别,或者结构体作为函数返回值的情况。另外,确保所有示例代码符合C语法,比如使用正确的括号和分号。还要提醒用户注意内存对齐对性能的影响,以及结构体复制时的深拷贝和浅拷贝问题,不过这可能属于进阶内容,暂时先不深入。 确保语言口语化,避免使用专业术语过多,但又要准确。分步骤讲解,结构清晰,让用户能够一步步理解结构体的各个方面。最后总结结构体的重要性,强调其在C语言中的灵活性和强大功能。</think>好的!下面我将分步骤详细讲解C语言中的结构体,帮助您逐步理解这一重要概念。 --- ### **一、结构体的基本概念** **结构体(Struct)** 是C语言中用于组合多个不同类型数据的用户自定义数据类型。它允许将相关联的数据项整合为单一逻辑单元。 **核心特点**: - 可包含多个不同类型的成员变量 - 成员在内存中按声明顺序连续存储(可能有内存对齐的间隙) - 通过点运算符(`.`)或箭头运算符(`->`)访问成员 --- ### **二、结构体的定义** #### 1. 语法格式 ```c struct 结构体名 { 数据类型 成员1; 数据类型 成员2; // ... }; ``` #### 2. 示例 ```c struct Student { int id; // 学号 char name[20]; // 姓名 float score; // 成绩 }; ``` --- ### **三、结构体变量声明与初始化** #### 1. 声明结构体变量 ```c // 方式1:先定义类型,再声明变量 struct Student stu1; // 方式2:定义类型时直接声明变量 struct Book { char title[50]; float price; } book1, book2; ``` #### 2. 初始化结构体 ```c // 按顺序初始化 struct Student stu2 = {1001, "Alice", 92.5}; // C99标准支持指定成员初始化(更清晰) struct Student stu3 = { .id = 1002, .name = "Bob", .score = 88.0 }; ``` --- ### **四、结构体成员的访问** #### 1. 使用点运算符(`.`) ```c struct Student stu; stu.id = 1003; strcpy(stu.name, "Charlie"); stu.score = 95.5; printf("ID: %d, Name: %s\n", stu.id, stu.name); ``` #### 2. 结构体指针与箭头运算符(`->`) ```c struct Student *pStu = &stu; pStu->score = 97.0; // 等价于 (*pStu).score ``` --- ### **五、结构体的内存对齐** #### 1. 对齐原则 - 结构体成员的起始地址必须是其类型大小的整数倍 - 结构体总大小是最大成员大小的整数倍 #### 2. 示例分析 ```c struct Example { char a; // 1字节(偏移0) // 填充3字节(对齐到4) int b; // 4字节(偏移4) double c; // 8字节(偏移8) }; // 总大小:16字节 ``` --- ### **六、结构体的嵌套** #### 1. 包含其他结构体 ```c struct Date { int year; int month; int day; }; struct Employee { int id; struct Date birthday; // 嵌套结构体 }; ``` #### 2. 自引用结构体(用于链表/树) ```c struct Node { int data; struct Node *next; // 指向自身类型的指针 }; ``` --- ### **七、结构体的应用场景** 1. **复杂数据建模**:如学生信息、商品信息 2. **文件I/O**:将结构体整体写入/读取二进制文件 3. **函数参数传递**:通过结构体指针高效传递大量数据 4. **数据结构实现**:链表、树、图的节点结构 --- ### **八、补充技巧** - **typedef简化写法**: ```c typedef struct Student { // ... } Student; Student stu4; // 无需再写struct关键字 ``` --- ### **总结** 结构体是C语言中组织复杂数据的核心工具。通过合理设计结构体,可以: 1. 提高代码可读性和可维护性 2. 更高效地管理关联数据 3. 为面向对象编程思想奠定基础 建议通过实际项目(如学生管理系统)练习结构体的综合使用!
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值