C语言深拷贝和浅拷贝问题

目前查到的资料,发现只有在结构体带指针成员,在拷贝结构体时(要用memcpy)时会出现。有些地方用结构体赋值代替memcpy结构体,虽然结果是一样的,但是,个人认为,赋值不叫拷贝,应该就叫赋值,否则浅拷贝情况太多了。

1、

下面这篇讲memcpy的部分应该是正解:

memset()函数与memcpy()函数知识总结---结构体中有指针变量_memcpy内存泄漏-CSDN博客

这里结构体里面的指针成员指向了动态分配内存。其实就算是指向任何一个变量或者数组,拷贝结构体时都是浅拷贝,即,拷贝结构体的指针成员本身(值)被拷贝,也就是其指向被拷贝结构体指针成员指向的值,而不指向它原来指向的值,原来指向的值就和这个指针脱离了。

所以这在释放内存时会出问题,会导致拷贝结构体指针成员原来指向的动态内存没有指针指向它,也基本就无法释放了,后面如果涉及动态内存申请和释放,程序会报错。而如果再按照常规流程,依次释放这两个结构体指针成员,则导致同一块内存(被拷贝结构体指针成员指向的动态内存)被释放两次,也会报错。

2、

看起来,会有些怀疑是结构体拷贝时拷贝字节数不够,只拷贝了结构体本身长度的字节,而没有拷贝结构体指针成员指向的动态内存的字节。程序改为:


struct stTest
{
	int a;
	int* p;
};

int main()
{
	stTest stTT1 = { 0 }, stTT2 = { 0 };
	stTT1.p = (int*)malloc(sizeof(int) * 100);
	stTT2.p = (int*)malloc(sizeof(int) * 100);//不可注释
	//memset(stTT1.p, 0, sizeof(int) * 100);
	//memset(stTT2.p, 0, sizeof(int) * 100);
	for (int i = 0; i < 100; i++)
	{
		stTT1.p[i] = 2 * i + 1;
	}
	// 执行结构体拷贝
	stTT1.a = 1;
	memcpy(&stTT2, &stTT1, sizeof(int) * 102); // 拷贝stTT1.p内存内容到stTT2.p的内存中

	for (int i = 0; i < 100; i++)
	{
		stTT2.p[i] = stTT2.p[i] + 1;
		std::cout << stTT2.p[i] << "\n";
	}
	for (int i = 0; i < 100; i++)
	{
		std::cout << stTT1.p[i] << "\n";
	}
	free(stTT1.p);
	free(stTT2.p);
	return 0;
}

调试运行。断点打在第20行:

此时两结构体的指针成员初始值(地址)是不一样的。

运行下一行,即结构体拷贝,字节数是包括100个int型动态内存字节的:

结构体指针成员值变一样了!所以,还是浅拷贝。那怕指针只是指向一个int变量,即单值的指针,拷贝结构体时依然是浅拷贝,即只拷贝指针值,不拷贝指针指向的值。

所以,只要是带指针的结构体拷贝,不管拷贝多少字节,都是浅拷贝。

继续运行到最后释放内存free语句,则会报错。

3、

下面,结构体指针指向一个数组,也是一样的浅拷贝:

struct stTest
{
	int a;
	int *p;
};

int main()
{
	stTest stTT1, stTT2;

	stTT1.a = 1;
	int x[10] = { 0 };

	stTT1.p = x;
	memcpy(&stTT2, &stTT1, sizeof(stTT1)); // 拷贝结构体
	return 0;
}

在依次将断点打在11、14 、15行时,调试结果如下:

赋值前变量成员a和指针成员p都是默认缺省值:

stTT1赋值后:

结构体拷贝后:

修改为结构体赋值:

//memcpy(&stTT2, &stTT1, sizeof(stTT1)); // 拷贝结构体
stTT2 = stTT1;

结果:

或:

	stTT2.a = stTT1.a; // 拷贝成员变量a
	stTT2.p = stTT1.p;

和浅拷贝一样的结果。

哪怕指针只是单个值,结构体拷贝也是浅拷贝。

4、

但是,结构体内如果是数组,则拷贝直接拷贝了数组值,而地址却不会拷贝:

struct stTest
{
	int a;
	int p[100];
};

int main()
{
	stTest stTT1 = { 0 }, stTT2 = { 0 };

	for (int i = 0; i < 100; i++)
	{
		stTT1.p[i] = 2 * i + 1;
	}
	// 执行结构体拷贝
	stTT1.a = 1;
	memcpy(&stTT2, &stTT1, sizeof(stTT1)); // 拷贝stTT1到stTT2

	for (int i = 0; i < 100; i++)
	{
		stTT2.p[i] = stTT2.p[i] + 1;
		std::cout << stTT2.p[i] << "\n";
	}
	for (int i = 0; i < 100; i++)
	{
		std::cout << stTT1.p[i] << "\n";
	}

	return 0;
}

5、


此外,如前面提到的,赋值的效果和浅拷贝一样,有些地方也说成是浅拷贝,如下:

C杂讲 浅拷贝 与 深拷贝_c语言 指针 深拷贝 浅拷贝-CSDN博客

也如上述,其实个人认为赋值不叫拷贝。否则,两个变量赋值,其实是值赋值,变量地址并不赋值,那么是深拷贝,但是两个指针赋值,则又变浅拷贝了。变来变去,不好统一。所以直接说成赋值了。带指针的结构体赋值,也会出现浅拷贝类似的问题。

另外,两块动态内存,拷贝时也只能是用memcpy加实际的内存字节数,而不能用赋值,赋值的结果,和上面浅拷贝一样,就是两个指针都指向一块内存(被拷贝的指针指向的),另一块内存(拷贝指针原来指向的)又没有指针指向了,再释放内存时就出问题了。

  • 23
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值