我编写了以下代码,它似乎与旧的std :: string实现工作正常。但是使用gcc 5.1,它会崩溃。
#include <string>
#include <iostream>
#include <cstring>
struct abc
{
public:
abc() {}
abc(const std::string& x)
: gullu(x)
{
}
std::string gullu;
};
int main()
{
abc *a = new abc("dhfghdf");
abc *b = new abc();
memcpy((void *)&b, (void *)&a, sizeof(abc));
std::cout << a->gullu.data() << std::endl;
std::cout << b->gullu.data();
return 0;
}
调试它,我发现在执行memcpy之后,对象'a'的内容变成了垃圾。
在建造之后,
(gdb)p * a $ 1 = {gullu = {static npos = 4294967295,_M_dataplus = {&GT; = {&lt; __ gnu_cxx :: new_allocator&gt; = {},}, _M_p = 0x804ea18“dhfghdf”},_ M_string_length = 7,{_M_local_buf =“dhfghdf \ 000 \ 000 \ 000 \ 000 \ 000 \ 000 \ 000 \ 000 \ 000”,_M_allocated_capacity = 1734764644}}}
b建造后
(gdb)p * b $ 2 = {gullu = {static npos = 4294967295,_M_dataplus = {&GT; = {&lt; __ gnu_cxx :: new_allocator&gt; = {},},_ M_p = 0x804ea38“”}, _M_string_length = 0,{_M_local_buf ='\ 000',_M_allocated_capacity = 0}}}
memcpy之后
(gdb)p * a $ 4 = {gullu = {static npos = 4294967295,_M_dataplus = {&GT; = {&lt; __ gnu_cxx :: new_allocator&gt; = {},}, _M_p = 0x666468},_ M_string_length = 2572404,{_ M_local_buf = “T @'\ 000 \030Ùÿ¿I\ 215 \ 004 \ B \ 001 \ 000 \ 000" , _M_allocated_capacity = 2572404}}}
(gdb)p * b $ 5 = {gullu = {static npos = 4294967295,_M_dataplus = {&GT; = {&lt; __ gnu_cxx :: new_allocator&gt; = {},}, _M_p = 0x804ea18“dhfghdf”},_ M_string_length = 7,{_M_local_buf =“dhfghdf \ 000 \ 000 \ 000 \ 000 \ 000 \ 000 \ 000 \ 000 \ 000”,_M_allocated_capacity = 1734764644}}}
我正在使用第三方库,它似乎在做一个memcpy,它正在使用以前的编译器而不使用gcc 5.1因为这个问题
有人可以帮我这个吗?
答案 0 :(得分:3)
嗯,你没有做你想做的事。虽然您显然正在尝试将string
的内容复制到另一个,但您正在做的只是将指针(a
)的值复制到另一个(b
),但是复制错误的尺寸!
顺便说一句,你的意图是memcpy(b, a, sizeof(abc));
,但这不应该也可以工作(虽然它会在几个案例中起作用)!正如我在我的皮肤上学到的(并且感谢this answer),你不能memcpy
一个具有非平凡初始化的对象。通过重复使用这样一个对象的存储,你可以终止它的生命周期,但只是memcpy
它就不会复活它,所以b
所指向的对象将不会活着
来自C ++ 11标准的引言(§3.8对象生存期 [basic.life] ):
对象的生命周期是对象的运行时属性。据说一个对象具有非平凡的初始化 如果它是一个类或聚合类型,它或它的一个成员是由一个非常重要的构造函数初始化的 默认构造函数。 [注意:通过简单的复制/移动构造函数进行初始化是非平凡的初始化。 - 结束注释]类型T的对象的生命周期开始于: - 获得具有适当对齐和T型尺寸的存储, 和 - 如果对象具有非平凡的初始化,则其初始化完成。 类型T的对象的生命周期在以下情况下结束: - 如果T是具有非平凡析构函数(12.4)的类类型,则析构函数调用开始,或者 - 对象占用的存储空间被重用或发布 。 §
答案 1 :(得分:2)
你正在复制sizeof(abc)
字节的数据,但你给memcpy的指针指向a
和b
的指针,而不是对象 - 这里的地址{{ 1}}肯定是错的。
但是你真的不应该这样做,因为复制像这样的对象而不是使用它们复制ctor或其他适当的方法将导致崩溃和未定义的行为。在这种特定情况下,如果字符串有点长,可能会发生两个(void*)&a
对象指向同一个堆块,因此您将获得退出范围的双重删除,这是一个不可恢复的崩溃。 / p>
答案 2 :(得分:2)
您的代码触发了未定义的行为,因为您向指针b
的地址复制的字节多于指针的大小:您正在复制sizeof(abc)
而不是sizeof(a)
。