在Linux上使用std :: map的内存对齐问题

时间:2011-08-14 16:35:09

标签: c++ memory alignment

我在使用Linux上的c ++时遇到了问题。

我有一个基本的Message类,如下所示:

class MsgBase
{
    public:
        MsgBase( unsigned int msgid );
        map < unsigned int, MSGIEBase* > messageIE_Map; // map for IEs
        unsigned int messageId; // 32 bit message id
};

类Der1派生自MsgBase,如下所示:

class Der1 : public MsgBase
{
    public:
        Der1 ();
        virtual ~Der1 ();

        // IEs
        MSGIE_UINT32 ueId;
        MSGIE_String configFileName;
};

这里MSGIE_UINT32和MSGIE_String是从MSGIEBase派生的类,因此它们的地址可以存储在上面基类中定义的映射中。 构造Der1时,ueId和configFileName的地址存储在地图中。 在这里,如果我打印地图的大小(通过gdb和在程序中)它将成为24。 [_M_header = 16,_M_node_count = 4,_M_key_compare = 1,我想是3字节填充]。

直到这里一切都很好。现在,Der1对象指针放在一个事件对象中,事件就会发布到队列中。事件类看起来像:

class Event 
{
   public:
       MsgBase* msgPtr;
};

另一个线程从队列中删除事件,提取msgPtr并将其强制转换为Der1指针,这就是问题开始的地方。

这里,如果我在程序中打印地图的大小,它就是21.这意味着MsgBase类中下一个成员的地址,即messageId被移动3个字节,因此messageId的值完全改变。 (当通过gdb看到时,地址仍然完好无损,地图的大小也是如此,即24)。

据我所知,这是一个单词对齐问题,但为什么内存对齐在不同的函数中不一致,为什么当使用new分配类的内存时,类成员的地址会变化。我使用的是Linux 2.6.27。 ,gcc版本4.1.1。 ,RHEL-4。

2 个答案:

答案 0 :(得分:1)

为了排除非虚拟析构函数/复制/分配问题,请将以下内容添加到MsgBase

public:
    virtual ~MsgBase();
private:
    MsgBase(MsgBase const& other);
    MsgBase& operator=(MsgBase const& other);

答案 1 :(得分:0)

我会尝试一步一步地提供所有必需的信息:

信息1:相关代码。

//Step 1:  Create msg and fill message Id
MsgBase*msgPtr = new Der1();

// C'tor of Der1 is as follows:
Der1::Der1 ()
          : MsgBase ( ATS_SUTD_EPCTESTER_ATTACH_SCENARIO_MsgId ), // msgid is 13( 0xd )
            ueId ( IE_UE_KEY, "UE", false ),
            configFileName ( IE_CONFIG_FILE_NAME_KEY, "Configuration File Name", false )
{
            // Insert the IEs in the map
this->addIEEntry ( IE_UE_KEY, &ueId ); // this puts entries in the map
this->addIEEntry ( IE_CONFIG_FILE_NAME_KEY, &configFileName );
}

// Step 2: Declare event and post the event
Event* event = new Event ( eventId, "Event" );
event->setData( msgPtr, hdr);

// check the message id at this stage ( 
cout << "msgId  = " << ( ( (Der1* )msgPtr )->messageId )<< endl; // Here it comes out 
                                                          // to be 0xd which is correct

// post the event
AppClass::getInstance()->addEventAndSchedule ( event );

//The queue is a member of AppClass and  has been defined as 
std::list <EventBase* > eventQueue;

// The code which inserts data into the queue is as follows:
bool AppClass::addEventAndSchedule ( EventBase* ev )
{
   if ( ev == NULL ) return false;
   this->eventQueueMutex.acquireLock();
   this->eventQueue.push_back( ev );
   this->eventQueueMutex.releaseLock();

   // Submit Job to Scheduler
   bool status = JobScheduler::getInstance()->scheduleJob( this );
   return status;
}

// The event class is
class Event: public EventBase
{
  public:
  Event ();
  virtual ~Event ();
  Event ( int evId );
  Event ( int evId, string evName );
  MsgBase* getMessagePtr ();
  void setData ( MsgBase*  mPtr, Header* hPtr )

  private:
   // Prevent copying
   Event& operator= ( Event& ev );
   Event ( Event& evBase );

   MsgBase* msgPtr;
   Header*    hdrPtr;
};

void Event::setData ( MsgBase* mPtr,  Header* hPtr )
{
   this->msgPtr = mPtr;
   this->hdrPtr =  hPtr;
}


Step 3 : Extract the event and re-print the message Id
// The code which extracts data from the queue is as follows:
void AppClass::process ()
{
               EventBase* beventPtr = NULL;
               this->eventQueueMutex.acquireLock();

    if ( !this->eventQueue.empty() )
    {
       beventPtr  = (EventBase* )( this->eventQueue.front() );
       this->eventQueue.pop_front();
    }
    else
    {
        isQueueEmpty = true;
    }

    this->eventQueueMutex.releaseLock();
    Event* eventPtr = ( Event* )beventPtr ;

                             Der1* msgPtr = (Der1* )( eventPtr->getMessagePtr()) ;
     cout << "msgId  = " <<  msgPtr->messageId << endl;  // This value
             //comes out to be incorrect it is now 0xd000000  i.e. a 3 byte shift

}

信息2:确切的问题。   确切的问题是'messasgeId'在转换中正在发生变化。最初它是0xd但是从队列中弹出后它变成0xd000000。因此,所有处理都停止了。在程序中打印时,此参数的地址也会从0x82bd7cc更改为0x82bd7c9。但是从gdb看,它仍然是0x82bd7cc,值仍然是0xd。

信息3:编译器标志。    编译器标志对于所有文件都是相同的,它们是:     -O0 -g3 -Wall -fmessage-length = 0