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

发布于 2024-11-29 12:36:27 字数 1195 浏览 0 评论 0原文

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

我有一个消息基类,如下所示:

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。 ,海湾合作委员会版本4.1.1。 ,RHEL-4。

I have run into a problem while working with c++ over Linux.

I have a base Message class which looks like this:

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

class Der1 is derived from MsgBase and looks like:

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

        // IEs
        MSGIE_UINT32 ueId;
        MSGIE_String configFileName;
};

Here MSGIE_UINT32 and MSGIE_String are classes derived from MSGIEBase and therefore their address can be stored in the map defined in base class above.
When Der1 is constructed the address of ueId and configFileName is stored in the map.
Here if I print the size of map ( through gdb and in the program ) it comes to be 24.
[ _M_header = 16, _M_node_count = 4, _M_key_compare = 1, 3 byte padding I suppose ].

Till here everything is fine. Now the Der1 object pointer is put inside an event object and the event is post into a queue. The event class looks like:

class Event 
{
   public:
       MsgBase* msgPtr;
};

A different thread removes the event from the queue, extracts the msgPtr and casts it into Der1 Pointer and this is where the problem starts.

Here if I print the size of the map in the program it is 21. That means the address of the next member in the MsgBase class i.e. messageId gets shifted by 3 bytes and so the value of messageId changes completely. (when seen through gdb, the address is still intact and so is the size of the map i.e. 24 ).

This is a word alignment issue to the best of my knowledge but why is the memory alignment not consistent in different functions and why does the address of a member of a class chage when the memory to the class has been allocated using new. I am using Linux 2.6.27. , gcc version 4.1.1. , RHEL-4.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

尬尬 2024-12-06 12:36:27

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

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

For ruling out non-virtual destructor/copy/assignment problems, please add the following to MsgBase:

public:
    virtual ~MsgBase();
private:
    MsgBase(MsgBase const& other);
    MsgBase& operator=(MsgBase const& other);
玻璃人 2024-12-06 12:36:27

我将尝试逐步提供所有必需的信息:

信息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 -f消息长度=0

I will try to provide all the required information step by step:

Information 1 : The relevant code.

//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

}

Information 2 : Exact problem.
The exact problem is that the 'messasgeId' is getting changed in transition. Initially it is 0xd but after popping from the queue it becomes 0xd000000. Because of this all the processing stops. The address of this parameter also changes from 0x82bd7cc to 0x82bd7c9 when printed in the program. However when seen from gdb it is still 0x82bd7cc and the value is still 0xd.

Information 3 : Compiler Flags.
Compiler Flags are same for all the files and they are:
-O0 -g3 -Wall -fmessage-length=0

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文