tags:

views:

43

answers:

3

I have the following casting problem when my data structure sSpecificData contains a field of type tm:

typedef struct
{
   unsigned char  data[10000];
} sDataBuffer;

typedef struct
{
   int                 m_Id;
   sDataBuffer         m_Data;
} sData;

typedef struct {
   int                       m_value1;
   int                       m_value2;
   tm                        m_Date;
} sSpecificData;

const int SPECIFIC_SVC_DATA_SIZE = sizeof(sSpecificData);

typedef struct {
   int                    m_Id;
   sSpecificData          m_Data;
} sMyData;

int main(void)
{
       sData svc;
       sMyData* ptr1 = (sMyData*) &svc;
       sSpecificData* ptr2;
       ptr2 = (sSpecificData*) &svc.m_Data;
       ptr1->m_Data.m_value1 = 90;
       ptr1->m_Data.m_value2 = 80;
       cout << ptr1->m_Data.m_value1 << endl;
       cout << ptr1->m_Data.m_value2 << endl;
       cout << ptr2->m_value1 << endl;
       cout << ptr2->m_value2 << endl;       
       return 0;
}

Without the field "tm m_Date;" as part of the sSpecificData, the output is correct:

90
80
90
80

With the field "tm m_Date;" as part of the sSpecificData, the output is wrong:

90
80
0   <-- !
90  <-- !

Is there any idea why my example doesn't work when there is field of type tm as part of the sSpecificData?

Thanks!

+2  A: 

this has to do with structure packing. by adding the tm field to the sSpecificData, you are changing it from a structure that has a natural alignment of int (4 bytes), to something that has a natural alignment of what appears to be 8 bytes.

so with tm as part of sSpecificData, the structure sMyData is effectively this

typedef struct {
   int                    m_Id;
   // 4 bytes of padding inserted to align sSpecificData on an 8 byte boundary.
   sSpecificData          m_Data; // with tm, this has a alignment of 8
} sMyData;

this explains what you are seeing. To fix this, you can add an explicit padding value to sMyData AND to sData.

typedef struct
{
   int                 m_Id;
   int                 m_padding;
   sDataBuffer         m_Data; // this accepts structures with an alignment of up to 8 
} sData;

typedef struct {
   int                    m_Id;
   int                    m_padding;
   sSpecificData          m_Data; // with tm, this has a alignment of 8
} sMyData;

Or you can use #pragma pack(4) (if your c compiler supports it) to force the padding to be left out of sMyData, but this will result in tm being misaligned which isn't a good idea. see this related question.

a very similar question came up today about C#, but the issues are the same, (the syntax for specifying packing is different though) http://stackoverflow.com/questions/2428168/size-of-structures-in-net/2428231#2428231

John Knoeller
+1  A: 

Generally such operations with memory are very platform-dependant.

Main problem is that due to data aligning variables in memory can be located not where you expect them to.

Glorphindale
A: 

Apart from already mentioned problems your code also violates the strict aliasing rule, therefore its behavior is undefined.

Let_Me_Be