新的std::string实现(gcc 5.1)

memcpy on new std::string implementation (gcc 5.1)

本文关键字:gcc 实现 std string 新的      更新时间:2023-10-16

我已经写了下面的代码,这似乎是工作良好的旧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'的内容变成了垃圾

a构造后,

(gdb) p *a $1 = {gullu = {static npos = 4294967295, _M_dataplus ={> = {<__gnu_cxx::new_allocator> = {},};_M_p = 0x804ea18 "dhfghdf"}, _M_string_length = 7, {_M_local_buf = "dhfghdf0000000000000000 0000000000000000 0000000000000000 00000000", _M_allocated_capacity = 1734764644}}

b构造后

(gdb) p *b $2 = {gullu = {static npos = 4294967295, _M_dataplus ={> = {& lt; __gnu_cxx:: new_allocator> = {}, }, _ M_p = 0 x804ea38 " "},_M_string_length = 0, {_M_local_buf = 000, _M_allocated_capacity = 0}}}

memcpy后

(gdb) p *a $4 = {gullu = {static npos = 4294967295, _M_dataplus ={> = {<__gnu_cxx::new_allocator> = {},};_M_p = 0x666468}, _M_string_length = 2572404, {_M_local_buf ="t@ 000 030 uy害怕我 215 004 b 001 000 000",_M_allocated_capacity = 2572404}}

(gdb) p *b $5 = {gullu = {static npos = 4294967295, _M_dataplus ={> = {<__gnu_cxx::new_allocator> = {},};_M_p = 0x804ea18 "dhfghdf"}, _M_string_length = 7, {_M_local_buf = "dhfghdf0000000000000000 0000000000000000 0000000000000000 00000000", _M_allocated_capacity = 1734764644}}

我正在使用第三方库,它似乎正在做一个memcpy,它与以前的编译器一起工作,而不是与gcc 5.1一起工作,因为这个问题

有人能帮我一下吗?

好吧,你没有做你想做的事。虽然您显然试图将string的内容复制到另一个,但您正在做的只是将指针(a)的值复制到另一个(b),但复制错误的大小!

顺便说一句,你想要的是memcpy(b, a, sizeof(abc));,但这是不应该工作太(它将在几个情况下工作)!正如我在皮肤上学到的(感谢这个答案),您不能memcpy具有非平凡初始化的对象。通过重用这样一个对象的存储,你结束了它的生命周期,但只是memcpy指向它,你不会复活它,所以由b指向的对象将不是活的。

引用自c++ 11标准(§3.8对象生命周期[basic.life]):

对象的生存期是对象的运行时属性。一个对象被称为具有非平凡初始化如果它是类或聚合类型,并且它或它的一个成员是由构造函数初始化的,而不是一个平凡的默认构造函数。[注:普通复制/移动构造函数的初始化是非普通初始化。]- - - - - -类型T的对象的生命周期开始于:-获得适合T类型的正确对齐和大小的存储,如果对象有非平凡初始化,则初始化完成。T类型对象的生命周期在以下情况结束:-如果T是具有非平凡析构函数(12.4)的类类型,则开始析构函数调用,或者- 对象占用的存储空间被重用或释放。§

您正在复制sizeof(abc)字节的数据,但是您给memcpy的指针指向ab的指针,而不是对象-这个(void*)&a中的地址绝对是错误的。

但是你真的不应该这样做,因为像这样复制对象而不是使用它们的复制函数或其他适当的方法会导致崩溃和未定义的行为。在这个特定的情况下,如果字符串长一点,可能会发生什么,你最终会有两个string对象指向同一个堆块,因此你会得到双重删除退出范围,这是一个不可恢复的崩溃。

您的代码触发未定义行为,因为您正在向指针b的地址复制比指针大小更多的字节:您正在复制sizeof(abc)而不是sizeof(a)