目前查到的资料,发现只有在结构体带指针成员,在拷贝结构体时(要用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加实际的内存字节数,而不能用赋值,赋值的结果,和上面浅拷贝一样,就是两个指针都指向一块内存(被拷贝的指针指向的),另一块内存(拷贝指针原来指向的)又没有指针指向了,再释放内存时就出问题了。