Sequential Memory - is this safe?

Started by
6 comments, last by Empirical 19 years, 8 months ago
I've always wondered this. Lets say we have a structure: struct Thing { float property1; float property2; int property3; int property4; } And then we allocate some of these structures in memory. For one structure, are all the properties in one contiguous memory space? Like property4 is right after property3, which is right after property2, etc. So it would be safe to do something like this. unsigned char *buffer = &thingInstance.property1 float retrievedProperty2 = buffer+sizeof(float); Would it be safe to bet that it is always the case- that the memory is contiguous for the structure?
Advertisement
No, because of packing. You might be able to disable the packing with compiler #pragmas or similar, but some cpu's can't cope with 32 bit accesses not on 32 bit boundaries.

You can always use a macro
#define OFFSET_OF( var_, field_ ) ( &var_.field_ - &var_ )

to get the offset in sizeof units.

e.g. OFFSET_OF(thingInstance, property2)

expands to &thingInstance.property2 - &thingInstance, which is what you want.
"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley
Not always, the compiler might do some padding to align the data for highest performance, ie:

struct
{
char a;
int b;
};

With default packing settings the compiler would probably insert 3 bytes of unused data between a and b to make sure b is on a 4-byte boundary. If it isnt, it will be slower to access. If you are using visual c++ you can change the settings with #pragma pack or in the project settings dialog, and there are similiar switches in other compilers as well.

Though in your case all your properties will be in contiguous memory since each one is 4 bytes in size and no padding is necessary.
Interesting. Thanks for the information. I probably won't end up using it, but just wanted to know if the technique is even available.

Actually, I might implement a function in my vector class that returns a float pointer. Currently the vector has members x, y, and z... They are not in a structure, but they are declared contiguously, and they are each 4 bytes. So I think it would still be safe to 'return &x'?
Actually this sort of thing is done in file loaders all the time.

struct fileheader{  long headerSize;  long dataSize  char someHeaderCrap[24];  long someMoreHeaderCrap;  short justALittleBitMoreCrap;  short padding;  byte[1] fileDataGoesHere;};// then, somewhere in your file loading code...fileheader* pFile = (fileheader*) new byte[totalFileSize];fread(pfile,totalFileSize,1,f);


As has already been said, you need to be aware of packing issues between different sized variables, and how to override this when it's necessary using #pragmas.

Also, if you just want to be able to access your data in different ways, then unions might be a useful thing. let's suppose you had a vector structure, and you wanted array access to the elements as well as explicit access (x, y z, etc...) you could do this using unions.

union vector3{   struct  {     float x;     float y;     float z;  };  float[3] value;};// later, in your codevector v;v.x = 8.5; // assign a value to x;v.value[1] = 32.7; // assign a value to y!;
you may have a look to the #pragma pack documentation on the msdn for further informations (check here).

Using VC++, what you'll want to do is :

[source lan="cpp"]// to align struct members a on 1-byte boundary#pragma pack(push, 1) struct MyStruct{    float memberA;    float memberB;    int   memberC;    int   memberD;};#pragma pack(pop)


Then your are sure that you do not have any kind of padding in your structure.

Alternatively, you may use a compiler optino to globaly change the packing alignement of structure members (/Zp1, /Zp2; /Zp4 or /Zp8. The msdn states that the default /Zp is /Zp8).

HTH
The ANSI standard defines the offsetof keyword, in <stddef.h>, to get the byte offset of structure members.
Also, the c/C++ compiler will never rearrange the order of data in a structure / class. It would lead to so much broken code, for example:
fwrite (&structure, sizeof structure, 1, file);

just wouldn't be possible.

Skizz
For VC on Windows I have found the following rule:

A veriable in a struct must have an offset equal to the multiple of its size.

Allow me to explain:

An int is 32bits (at the moment) so it MUST have an offset (the total bytes that preceeded it, including padding) of either 4, 8, 12 bytes and so on.

A short must be a multiple of 2 (2, 4, 6, 8)

A char can be anywhere. If you break this rule the compiler pads the structure to move the veriable to the correct positon. e.g if you place an int 13 bytes into your structure it will moved to 16 bytes (3 bytes will be inserted before it)

Example

struct Test{   int V1;   int V2; //this is 4 bytes into the structure, thats ok   char C1; //this ok a char can go anywhere   int V3;// this is wrong. V3 is 9 bytes into the    //the structure. 9 is not a multiple of 4, the next is 12.    //so 3 bytes will be added after C1.}

This topic is closed to new replies.

Advertisement