[Solved][Moved] The size of variables seems to be different than expected
-
I'm working on an application that's going to use shared memory. I haven't gotten into the shared memory thing yet; I'm just setting up a test that maps one structure on top of another. I have a structure called 'first' that's simply an array of 75 unsigned chars. I then have another structure called 'mainPtr' that has mixed ints and unsigned chars. Here's the definition for both of these variables:
@
typedef struct
{
unsigned char a;
int b;
unsigned char c;
} mainStruct;
mainStruct *mainPtr;
typedef struct
{
unsigned char byteValue[75];
} sharedStruct;
sharedStruct first;
@
I put controlled values in the 'first' structure, map 'mainPtr' to the 'first' structure, and then look at my results. Here's the code that does this:
@
first.byteValue[0] = 22; first.byteValue[1] = 0; first.byteValue[2] = 0; first.byteValue[3] = 0;
first.byteValue[4] = 1; first.byteValue[5] = 23; first.byteValue[6] = 1; first.byteValue[7] = 0;
mainPtr = (mainStruct *) &first;
ui->label->setNum(mainPtr->a);
ui->label_2->setNum(mainPtr->b);
ui->label_3->setNum(mainPtr->c);
@
The result I get for 'a' is exactly what I expected: 22. But the results I get for 'b' and 'c' aren't at all what I expected: 71425 and 112. I figured an int in Qt would take up either 2 or 4 bytes, but it doesn't seem to be either. How is the application arriving at the results I see? -
This really has nothing to do with Qt, does it?
And I'm not really sure it has anything to do with shared memory either...
It has to do with casting. In this case C-style casting, which is something you probably want to avoid in C++. What you probably want to use is a reinterpret_cast.
It also has to do with memory alignment, which may differ depending on your platform and compiler. -
Well, this problem is neither Qt-related nor is it caused by casting (reinterpret_cast and c style casts are basically the same).
- Your code does not respect platform-dependent and compiler-dependent storage sizes.
The C++ standard (and the C standard as well) does not define absolute sizes for storages (exception is char, which is always one byte), it defines relative minimum sizes. Thus int needs not to be 2 or 4 bytes. It may be 1, 2, 4, 8, 16 or any bytes.
This is solely based on the compiler/platform/architecture combination and is not influenced by Qt. However, Qt provides platform-independent storages which are guaranteed to have an absolute size (quint8, quint16 and so forth).
- Your code does not respect alignment.
The compiler aligns struct members on (architecture-dependant) natural word boundaries, for example 32 bit or 4 byte. So mainStruct.a is at byte 0, mainStruct.b is at byte 4 (not 1, 4 byte alignment), mainStruct.c is at byte 8 (not 5, 4 byte alignment).
This has two reasons: Most architectures trap on non-aligned memory access. This means, that on an architecture with 4 byte word size you cannot access a single byte in memory. You will have to read a whole word and mask out the unwanted bytes. This is done by the compiler for you - which leads to the second reason for alignment: performance. Fetching a single byte consumes way more processing power then fetching a whole word (for the reason mentioned above). So operating on a storage-size smaller then your natural word size is usually the first ingredient for a serious performance problem.
If you need strict memory alignment (or even bitwise alignment) you will have to use compiler-specific pragmas and extensions.
- Your code does not respect endianess.
The 32 bit int value 0xAABBCCDD might be either stored as 4 8 bit char values 0xAA, 0xBB, 0xCC, 0xDD or 0xDD, 0xCC, 0xBB, 0xAA - depending on the endianess. Whereas x86 is usually always little-endian, any ARM found in SoCs, mobile phones, boards might be little-endian or big-endian.
You will have to respect all of these things to get working and portable code. Shared memory takes any kind of structured data. Why do you transform it to an unstructured chuck of bytes in the first place?
-
All the above is true, and not to get into all the pitfalls, but it might be worth noting to the original poster that there are usually compiler specific struct/variable attributes that can be used to pack the structure such that there's virtuhttp://developer.qt.nokia.com/forums/viewthread/12022/#ally no padding between struct members.
e.g.
- on gcc it's struct { ... } attribute ((packed));
- on M$ compiler I believe it's a #pragma
Look it up before using. Don't expect portability.
-
I have tried to figure out what you want, but me is totally unclear your code snippet. I used Intel Pentium M Processor with Ubuntu 10.04 and my g++ is 4.4.3. My Qt is 4.7.4, 32bit.
I'm curious what your "solved" mean...
What I have tried:
@
#include <iostream>--using namespace std;
int main()
{unsigned char aa; int bb; unsigned char cc; typedef struct { unsigned char a; int b; unsigned char c; } __attribute__((__may_alias__)) mainStruct; mainStruct* mainPtr; typedef struct { unsigned char byteValue[75]; } sharedStruct; sharedStruct first; first.byteValue[0] = 22; first.byteValue[1] = 0; first.byteValue[2] = 0; first.byteValue[3] = 0; first.byteValue[4] = 1; first.byteValue[5] = 23; first.byteValue[6] = 1; first.byteValue[7] = 0; mainPtr = (mainStruct*) &first; //mainPtr = reinterpret_cast<mainStruct*>(&first); //1, 4, 1 byte = 6 byte cout << "size of aa,bb,cc: " << sizeof(aa) << ", " << sizeof(bb) << ", " << sizeof(cc) << endl; //6 cout << "size of sum of aa,bb,cc: " << sizeof(aa) + sizeof(bb) + sizeof(cc) << endl; //4 cout << "size of mainPtr: " << sizeof(mainPtr) << endl; //12 cout << "size of *mainPtr: " << sizeof(*mainPtr) << endl; //75 cout << "size of sharedStruct: " << sizeof(sharedStruct) << endl; //"empty" cout << "mainPtr->a " << mainPtr->a << endl; //71425 cout << "mainPtr->b " << mainPtr->b << endl; //"whatiscalled character, a question mark like char..." cout << "mainPtr->c " << mainPtr->c << endl; // Trying to print original values need a back casting: //casting BACK to sharedStruct, but this is not a pointer!, completely wrong... //sharedStruct st = reinterpret_cast<sharedStruct>(mainPtr); return 0;
}
@
-
Maybe you'd like to sg like this:
@
#include <iostream>using namespace std;
struct mainStruct
{
unsigned char a;
int b;
unsigned char c;
};struct sharedStruct
{
unsigned char byteValue[75];
} attribute((may_alias)) ;void print(mainStruct* x)
{
cout << x->a << endl;
cout << x->b << endl;
cout << x->c << endl;
}int main()
{mainStruct ms; //print(&ms); sharedStruct* ss = reinterpret_cast<sharedStruct*>(&ms); ss->byteValue[0] = 22; ss->byteValue[1] = 0; ss->byteValue[2] = 0; ss->byteValue[3] = 0; ss->byteValue[4] = 1; ss->byteValue[5] = 23; ss->byteValue[6] = 1; ss->byteValue[7] = 0; print(reinterpret_cast<mainStruct*>(ss)); return 0;
}
@