Search code examples
c++memoryreinterpret-cast

Reinterpreted pointer does not point to correct memory location


Suppose I got this in Message.h:

#ifndef _MESSAGE_H_
#define _MESSAGE_H_
#include <stdio.h>
#include <string.h>
enum PRIMITIVE{
      MESSAGE_1 = 100,
      MESSAGE_2,
};

enum { MSG_SIZE_IN_BYTES = 1024 };

class Header{
    protected:
        Header(PRIMITIVE prim, u_int32 transNum) : m_primitive(prim), m_transNum(transNum) { }
    public:
        // access
        PRIMITIVE primitive() const { return m_primitive; }
        u_int32 transNum()  const { return m_transNum; }
        virtual ~Header(){}
    private:
        PRIMITIVE m_primitive;
        u_int32 m_transNum;
    };

class Message
{
public:
    Message() { reset(); }
    // access
    char* addr() { return reinterpret_cast<char*>(m_buffer); }
    const char* addr() const { return reinterpret_cast<const char*>(m_buffer); }
    u_int32 size() { return sizeof(m_buffer); }
    // msgs
    Header* msgHeader() { return reinterpret_cast<Header*>(addr()); }
    const Header* msgHeader() const { return reinterpret_cast<const Header*>(addr()); }
    // modify
    void reset() {
        memset(&m_buffer, 0, MSG_SIZE_IN_BYTES);
    }
private:
    u_int64 m_buffer[MSG_SIZE_IN_BYTES / sizeof(u_int64)];
};
#endif

In main.cpp, I casted the m_buffer in Message into Header type. The point is that I can access memory according to Header layout:

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
int main(int argc, char *argv[]){
    Message msg;

    char* content = msg.addr();
    int prim = 100;
    int trans_num = 1;

    memcpy(content, &prim, 4);
    memcpy(content+4, &trans_num, 4);       

    const Message::Header* hdr = msg.msgHeader();
    Message::PRIMITIVE hdr_prim = hdr->primitive();
    u_int32 hdr_transNum = hdr->transNum();

    cout << "Memory address of Message: " << &msg << endl;

    cout << "Memory address of Message buffer: " << (void*) msg.addr() << endl;             
    cout << "Memory address of content: " <<  content << endl;      

    cout << "Memory address of Header: " << hdr << endl;
    cout << "Memory address of m_primitive in Header: " <<  &hdr_prim << endl;
    cout << "Memory address of m_transNum in Header: " <<  &hdr_transNum << endl;

    cout << "Primitive in Header: " << prim << endl;        
    cout << "Trans num in Header: " << transNum << endl;
}

The Header* hdr is supposed to point to the same memory address as Message msg, and the m_primitive is supposed to be at the same address as Header* hdr, and the m_transNum is &m_primitive + 4.

However, this is the actual value:

Memory address of Message: 0x699520

Memory address of Message buffer: 0x699520
Memory address of content: 0x699520

Memory address of Header: 0x699520
Memory address of m_primitive in Header: 0x7f2ec2f2738c
Memory address of m_transNum in Header: 0x7f2ec2f27388

Primitive in Header: 1
Trans num in Header: 1953719668

m_primitive and m_transNum pointed to a completely random location and got garbage values! How can it happen? reinterpret_cast is supposed to change the layout according to class type, by casting to differnt type of pointer.

Also, if it returns a copy, the value of m_primitive should be 100, and m_transNum should be 1, because I memcpy into the buffer char* content of msg. But the values are wrong.


Solution

  • Message::PRIMITIVE hdr_prim = hdr->primitive();
    u_int32 hdr_transNum = hdr->transNum();
    
    ...
    
    cout << "Memory address of m_primitive in Header: " <<  &hdr_prim << endl;
    cout << "Memory address of m_transNum in Header: " <<  &hdr_transNum << endl;
    

    You are printing addresses of local stack variables to which you assigned values of your Header data members. These addresses are not related to addresses of original variables.

    Try (see EDIT below with regard to virtual destructor)

    const Message::Header* hdr = msg.msgHeader();
    Message::PRIMITIVE * hdr_prim = ( Message::PRIMITIVE * ) hdr;
    u_int32 * hdr_transNum = ( u_int32 *) ( ( ( char * ) hdr ) + sizeof( Message::PRIMITIVE ) );
    
    ...
    
    cout << "Memory address of m_primitive in Header: " <<  hdr_prim << endl;
    cout << "Memory address of m_transNum in Header: " <<  hdr_transNum << endl;
    

    That's under assumption there is no padding. But enum field size should be int, usually. So it's 4 bytes and there is no padding. But check it to be sure.

    EDIT: I just saw you have virtual destructor in Header. The above won't work, since there is a pointer to vtable insider Header object. Where it is placed is compiler specific. You can try some experimenting and adjust your offsets accordingly.