我可以将char *缓冲区转换为对象类型吗?

时间:2011-01-06 10:31:19

标签: c++ memory constructor casting

我出于好奇而不是困难来问这个问题,因为我总是向你学习,即使是在不相关的主题上。

因此,请考虑以下方法,用C ++编写并与g ++链接。这个方法工作正常,因为所有内容都被初始化为正确的大小。

extern "C" 
  {
    void retrieveObject( int id, char * buffer )
      {
        Object::Object obj;

        extractObject( id, obj );
        memcpy( buffer, &obj, sizeof(obj) );
      }
  }

// Prototype of extractObject
const bool extractObject( const int& id, Object::Object& obj ) const;

现在,我想避免声明本地Object并使用memcpy

我尝试用以下内容替换retrieveObject

void retrieveObject( int id, char * buffer )
  {
    // Also tried dynamic_cast and C-Style cast
    extractObject( id, *(reinterpret_cast<Object::Object *>(buffer)) );
  }

它成功编译和链接,但立即崩溃。考虑到我的缓冲区足够大以容纳Object,C ++是否需要调用构造函数来“塑造”内存?是否有另一种方法来替换局部变量和memcpy?

我希望我很清楚你能回答,谢谢你。

5 个答案:

答案 0 :(得分:3)

在你的第一次努力......

void retrieveObject( int id, char * buffer )
{
     Object::Object obj;
     extractObject( id, obj );
     memcpy( buffer, &obj, sizeof(obj) );
} 

...你仍然让编译器创建局部变量obj,它保证正确对齐。在第二次努力......

void retrieveObject( int id, char * buffer )
{
     extractObject( id, *(reinterpret_cast<Object::Object *>(buffer)) );
} 

...你承诺编译器缓冲区指向一个为Object :: Object正确对齐的字节。但它会吗?可能不是,因为你的运行时崩溃。通常,char * s可以从任何给定的字节开始,因为更复杂的对象通常与字大小对齐或者与数据成员所需的最大对齐。在Object :: Object内部读取/写入整数,双精度,指针等只能在内存正确对齐时起作用 - 它取决于您的CPU等,但在UNIX / Linux上,未对齐可能会产生例如SIGBUS或SIGSEGV信号。

为了解释这一点,让我们考虑一个简单的CPU /内存架构。假设在任何给定的操作中,内存允许从地址0-3,4-7或8-11等读取4个字节(32位架构),但是你不能在地址读取4字节的卡盘1-4,2-5,3-6,5-8 ......听起来很奇怪,但这实际上是记忆的常见限制,所以只需接受并考虑后果。如果我们想在内存中读取一个4字节的数字 - 如果它在4个多个地址中的一个,我们可以在一个内存中读取它,否则我们必须读取两次:从一个包含部分的4字节区域数据,然后是包含其余部分的另一个4字节区域,然后丢弃我们不想要的位,并在适当的位置重新组合其余部分,以将32位值输入CPU寄存器/存储器。这太慢了,因此语言通常会谨慎地将所需的值放在内存可以在一次操作中访问它们的位置。甚至CPU都是按照这种期望设计的,因为它们通常具有直接对存储器中的值进行操作的指令,而无需将它们明确地加载到寄存器中(即,甚至是汇编/机器代码级别下的实现细节)。要求CPU对未对齐的数据进行操作的代码通常会导致CPU产生中断,OS可能会将其显示为信号。

尽管如此,关于在非POD数据上使用此安全性的其他警告也是有效的。

答案 1 :(得分:2)

您正在做的是有效地序列化Object,并且当{且仅当<{strong>> Object中的所有数据连续存储时,它将正常工作。对于简单对象,这将工作正常,但只要有包含指向其他对象的指针的对象,就会停止工作。

在C ++中,对象包含其他对象是非常常见的。 std::string就是一个很好的例子。 string类是一个容器,它引用存储在别处的引用计数器对象。因此,除非您确定该对象是一个简单的连续对象,否则请不要这样做。

答案 2 :(得分:1)

这可能有很多问题 - 首先,如果你使用本地对象,你不能只构造它,然后在其上写一些其他实例的内存(这只适用于POD类型,因为它们可以不需要调用析构函数),否则你很可能会遇到令人讨厌的内存泄漏。

但这不是主要问题 - 根据所用对象的类型,您提供的解决方案可能或可能不起作用。它适用于简单的POD类型,它甚至可以用于更复杂的类(假设您将正确处理构造函数/析构函数调用),但它会在程序的其他部分期望对象处于它的原始状态时中断location - 比方说,你有一个有2个成员变量的类:

struct A {
   int i;
   int * pi;
}

其中'pi'将始终指向'i'成员 - 如果您将该对象“memcpy”到其他位置,它将很容易破坏。

答案 3 :(得分:1)

您应该查看boost.serialisationboost::message_queues。 C ++对象包含多个运行时特定的数据(virtual tables)。

模块之间传输对象时,还应考虑添加有关对象的版本信息。

答案 4 :(得分:1)

找出崩溃的原因和地点,使用调试器。代码看起来还不错。

如果要避免中间对象实例,则只需避免它。让extractObject()返回指向Object的指针,并使用此指针将memcpy()的内容添加到buffer

但是要注意,正如其他人所说,如果你只是reinterpret_cast<> buffer回到Object,如果对象不够简单,事情可能会中断。