C语言结构体成员赋值的深拷贝与浅拷贝详解

目录
  • 浅拷贝
    • 结构体中不存在指针成员变量时
    • 结构体中存在指针成员变量时
  • 深拷贝
  • 结论

浅拷贝

C语言中的浅拷贝是指在拷贝过程中,对于指针型成员变量只拷贝指针本身,而不拷贝指针所指向的目标,它按字节复制的。我们分几种情况举例子来看一下。

结构体中不存在指针成员变量时

代码如下:

#include <stdio.h>
typedef struct {
    char name[64];
    int age;
}Member;

int main(){
    Member stu1 = { "LiMing", 18 };
    Member stu2;
    stu2 = stu1;
    printf("%s,%d\n", stu2.name, stu2.age);
    system("pause");
    return 0;
}

运行如下:

结构体中存在指针成员变量时

代码如下:

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

typedef struct {
 char *name;
 int age;
}Member;

int main() {
 Member Member1, Member2;
 Member1.name = malloc(sizeof(char) * 64);
 if (NULL == Member1.name)
 {
  printf("malloc failed\n");
 }
 memset(Member1.name, 0, 64);
 //strcpy(Member1.name, "LiMing");
 snprintf(Member1.name, 64, "LiMing");
 Member1.age = 18;

 Member2 = Member1;/*拷贝*/
 snprintf(Member2.name, 64, "LiXiaoYao");
 Member2.age = 29;

 printf("%s, %d\n", Member1.name, Member1.age);

 if (NULL != Member1.name) {
  free(Member1.name);
  Member1.name = NULL;
 }

 system("pause");
 return 0;
}

运行如下:

从中我们看到,改变Member2的值,Member1的值也改变了,这说明一片空间被两个不同的子对象共享了,改变一个对象的值另外一个也会随之改变。

我们改变Member2写法,申请内存的代码如下:

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

typedef struct {
 char *name;
 int age;
}Member;

int main() {
 Member Member1;
 Member1.name = malloc(sizeof(char) * 64);
 if (NULL == Member1.name)
 {
  printf("malloc failed\n");
 }
 memset(Member1.name, 0, 64);
 //strcpy(Member1.name, "LiMing");
 snprintf(Member1.name, 64, "LiMing");
 Member1.age = 18;

  Member Member2;
 Member2.name = malloc(sizeof(char) * 64);
 if (NULL == Member2.name)
 {
  printf("malloc failed\n");
 }
 memset(Member2.name, 0, 64);
 //strcpy(Member2.name, "LiMing");
 snprintf(Member2.name, 64, "LiXiaoYao");
 Member2.age = 29;

 Member1 = Member2;

 printf("%s, %d\n", Member2.name, Member2.age);

 if (NULL != Member1.name) {
  free(Member1.name);
  Member1.name = NULL;
 }
 if (NULL != Member2.name) {
  free(Member2.name);
  Member2.name = NULL;
 }
 system("pause");
 return 0;
}

运行如下:

从中我们看到,当数据成员中有指针时,两个类中的两个指针将指向同一个地址,当对象快结束时,会调用两次free函数,此时Member2已经是野指针(图中有X的错误标志),这个野指针指向的内存空间已经被释放掉,再次释放会报异常错误,要解决这个问题就要涉及到深拷贝了。

深拷贝

深拷贝除了拷贝其成员本身的值之外,还拷贝成员指向的动态内存区域内容,深拷贝会在堆内存中另外申请空间来储存数据。

解决的思路是在释放掉被赋值指针变量的旧指向内存时,重新对其开辟新内存,这种情况下两个结构体中指针地址不同,但是指向的内容是一致的。代码如下:

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

typedef struct {
 char *name;
 int age;
}Member;

int main() {
 Member Member1;
 Member1.name = malloc(sizeof(char) * 64);
 if (NULL == Member1.name)
 {
  printf("malloc failed\n");
 }
 memset(Member1.name, 0, 64);
 //strcpy(Member1.name, "LiMing");
 snprintf(Member1.name, 64, "LiMing");
 Member1.age = 18;

  Member Member2;
 Member2.name = malloc(sizeof(char) * 64);
 if (NULL == Member2.name)
 {
  printf("malloc failed\n");
 }
 memset(Member2.name, 0, 64);
 //strcpy(Member2.name, "LiMing");
 snprintf(Member2.name, 64, "LiXiaoYao");
 Member2.age = 29;

 if (Member1.name != NULL) {
  free(Member1.name);
  Member1.name = NULL;
 }
 Member1.name = malloc(strlen(Member2.name) + 1);
 strcpy(Member1.name, Member2.name);

 printf("%s, %d\n", Member1.name, Member1.age);

 if (NULL != Member1.name) {
  free(Member1.name);
  Member1.name = NULL;
 }
 if (NULL != Member2.name) {
  free(Member2.name);
  Member2.name = NULL;
 }
 system("pause");
 return 0;
}

运行如下:

结论

使用C语言来说,深拷贝浅拷贝的概念我们不需要深究,在进行结构体拷贝的时候,结构体成员是非指针的话,那么直接赋值是没有任何问题的,建议使用这种方式,避免浅拷贝这类不易发现的错误产生。

如果成员有指针类型,我们就需要重写拷贝函数,自己定义拷贝行为了,这一点我们需要尤为注意。

到此这篇关于C语言结构体成员赋值的深拷贝与浅拷贝详解的文章就介绍到这了,更多相关C语言深拷贝 浅拷贝内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解C语言内核字符串拷贝与比较

    在上一篇文章<驱动开发:内核字符串转换方法>中简单介绍了内核是如何使用字符串以及字符串之间的转换方法,本章将继续探索字符串的拷贝与比较,与应用层不同内核字符串拷贝与比较也需要使用内核专用的API函数,字符串的拷贝往往伴随有内核内存分配,我们将首先简单介绍内核如何分配堆空间,然后再以此为契机简介字符串的拷贝与比较. 首先内核中的堆栈分配可以使用ExAllocatePool()这个内核函数实现,此外还可以使用ExAllocatePoolWithTag()函数,两者的区别是,第一个函数可以直接分配内

  • C语言数据结构之复杂链表的拷贝

    题目: 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点. 构造这个链表的 深拷贝. 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值.新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态.复制链表中的指针都不应指向原链表中的节点 . 例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y

  • C语言实现字符串拼接和拷贝

    本文实例为大家分享了C语言实现字符串拼接和拷贝的具体代码,供大家参考,具体内容如下 字符串拼接: #include<stdio.h> #include<stdlib.h> #include<string.h> char *str_contact(const char *,const char *); char *str_contact(const char *str1,const char *str2) { char *result = (char*) malloc(s

  • C语言结构体成员赋值的深拷贝与浅拷贝详解

    目录 浅拷贝 结构体中不存在指针成员变量时 结构体中存在指针成员变量时 深拷贝 结论 浅拷贝 C语言中的浅拷贝是指在拷贝过程中,对于指针型成员变量只拷贝指针本身,而不拷贝指针所指向的目标,它按字节复制的.我们分几种情况举例子来看一下. 结构体中不存在指针成员变量时 代码如下: #include <stdio.h> typedef struct { char name[64]; int age; }Member; int main(){ Member stu1 = { "LiMing&

  • c++ STL之list对结构体的增加,删除,排序等操作详解

    对STL中的list进一步学习,编程过程中对结构体的操作很多. 全部代码如下: /* Project:list对结构体的使用 Date: 2018/07/14 Author: Frank Yu 常用函数:int size() 返回容器元素个数:bool empty() 判断容器是否为空,true为空: 增加函数:void push_back(元素) 尾元素后增加一个元素:push_front(元素) 首元素前增加一个元素: iterator insert(lit,元素)在迭代器指针lit前插入元

  • JavaScript中深拷贝与浅拷贝详解

    目录 1 浅拷贝概念 2 深拷贝概念 3 浅拷贝的实现方式 3.1 Object.assign() 3.2 Array.prototype.concat() 3.3 Array.prototype.slice() 3.4 直接赋值 4 深拷贝的实现方式 4.1 JSON.parse(JSON.stringify()) 4.2 函数库lodash 总结 1 浅拷贝概念 深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的. 浅拷贝是创建一个新对象,该对象有着原始对象属性值的一份精确拷

  • C++拷贝构造函数(深拷贝与浅拷贝)详解

    对于普通类型的对象来说,它们之间的复制是很简单的,例如:int a=88;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.下面看一个类对象拷贝的简单例子. 复制代码 代码如下: #include <iostream>using namespace std; class CExample {private:    int a;public:    CExample(int b)    { a=b;}    void Show ()    {       

  • Python对象的深拷贝和浅拷贝详解

    本文内容是在<Python核心编程2>上看到的,感觉很有用便写出来,给大家参考参考! 浅拷贝 首先我们使用两种方式来拷贝对象,一种是切片,另外一种是工厂方法.然后使用id函数来看看它们的标示符 复制代码 代码如下: # encoding=UTF-8   obj = ['name',['age',18]] a=obj[:] b=list(obj) for x in obj,a,b:     print id(x)   35217032 35227912 29943304 他们的id都不同,按照正

  • Python中的深拷贝和浅拷贝详解

    要说清楚Python中的深浅拷贝,需要搞清楚下面一系列概念: 变量-引用-对象(可变对象,不可变对象)-切片-拷贝(浅拷贝,深拷贝) [变量-对象-引用] 在Python中一切都是对象,比如说:3, 3.14, 'Hello', [1,2,3,4],{'a':1}...... 甚至连type其本身都是对象,type对象 Python中变量与C/C++/Java中不同,它是指对象的引用,Python是动态类型,程序运行时候,会根据对象的类型 来确认变量到底是什么类型. 单独赋值: 比如说: 复制代

  • javascript深拷贝和浅拷贝详解

    一.数组的深浅拷贝 在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问题的发生. 这是为什么呢? 因为如果只是简单的赋值,它只是进行了地址的引用,所以改变一个另一个也会跟着变. var arr = ["One","Two","Three"]; var arrto = arr; arrto[1] = "te

  • Java Cloneable接口的深拷贝与浅拷贝详解

    目录 Cloneable接口源码 浅拷贝案例 Pet类定义 Person类定义 浅拷贝问题-代码测试 深拷贝案例 Pet类重写clone()方法 Person的clone()方法中调用Pet的clone方法 浅拷贝问题解决-深拷贝代码测试 总结 Cloneable接口源码 Cloneable接口: 实现此接口的类——可被推断java.lang.Object的clone()方法可以被合法调用-以实现类实例:属性到属性的拷贝. 如果一个类未实现Cloneable接口,那么调用clone()方法时,会

  • Java Cloneable接口的深拷贝与浅拷贝详解

    目录 Cloneable接口源码 浅拷贝案例 Pet类定义 Person类定义 浅拷贝问题-代码测试 深拷贝案例 Pet类重写clone()方法 Person的clone()方法中调用Pet的clone方法 浅拷贝问题解决-深拷贝代码测试 总结 Cloneable接口源码 Cloneable接口: 实现此接口的类——可被推断java.lang.Object的clone()方法可以被合法调用-以实现类实例:属性到属性的拷贝. 如果一个类未实现Cloneable接口,那么调用clone()方法时,会

  • C语言结构体数组常用的三种赋值方法(包含字符串)

    目录 一.按照成员变量进行赋值(麻烦,好理解,字符串赋值需要strcpy) 二.对数组整体进行赋值.(一次性需要把所有的都添加进去,不需要strcpy) (1) 在声明数组的时候,进行赋值 (2)对有规律的数据赋值,比如学生结构体的学号是有规律的. 三.使用输入进行赋值 总结 一.按照成员变量进行赋值(麻烦,好理解,字符串赋值需要strcpy) 这里使用了一个Init函数,为了在进一步说明传参的使用.实际上赋值按照需要放在主函数就行. (使用strcpy函数需要添加头文件string.h) #i

随机推荐