Reputation: 599
My code has a structure type-defined as follows:
typedef struct
{
Structure_2 a[4];
UCHAR b;
UCHAR c;
}Structure_1;
where the definition of Structure_2 is as follows:
typedef struct
{
ULONG x;
USHORT y;
UCHAR z;
}Structure_2;
There are also two functions in the code. The first one (named setter) declares a structure of type “Structure_1” and fills it with the data:
void setter (void)
{
Structure_1 data_to_send ;
data_to_send.a[0].x = 0x12345678;
data_to_send.a[0].y = 0x1234;
data_to_send.a[0].z = 0x12;
data_to_send.a[1].x = 0x12345678;
data_to_send.a[1].y = 0x1234;
data_to_send.a[1].z = 0x12;
data_to_send.a[2].x = 0x12345678;
data_to_send.a[2].y = 0x1234;
data_to_send.a[2].z = 0x12;
data_to_send.a[3].x = 0x12345678;
data_to_send.a[3].y = 0xAABB;
data_to_send.a[3].z = 0x12;
data_to_send.b =0;
data_to_send.c = 0;
getter(&data_to_send);
}
The compiler saves data_to_send in memory like that:
The second one named getter:
void getter (Structure_1 * ptr_to_data)
{
UCHAR R_1 = ptr_to_data -> b;
UCHAR R_2 = ptr_to_data -> c;
/* The remaining bytes are received */
}
I expect that R_1 will have the value “00”, and R_2 will have the value “00”.
But what happen is the compiler translates the following two lines like that:
/* Get the data at the address ptr_to_data -> b,
which equals the start address of structure + 28 which contains the
value “AA”, and hence R_1 will have “AA” */
UCHAR R_1 = ptr_to_data -> b;
/* Get the data at the address ptr_to_data -> c,
which equals the start *address of structure + 29 which contains the
value “BB”, and hence R_2 will *have “BB” */
UCHAR R_2 = ptr_to_data -> c;
The compiler adds padding b/yte while saving the structure in stack, However when it starts reading it, it forget what it did (and includes the padding bytes in reading).
How could I inform the compiler that you should skip the padding byte while reading the elements of structure ?
I don't want a work around to solve this problem, I am curious to know why the compiler behaves like that ?
My compiler is GreenHills and My target is 32-bit
Upvotes: 0
Views: 929
Reputation: 12047
The longer version of my comment to @ryykers good answer:
The code you have shown in your question is perfectly valid, there is absolutely no reason why you would get the wrong values when reading the struct members in getter
, provided
Otherwise the compiler you are using would be severly broken.
The way to set the packing rules differ from compiler to compiler, they are not standardized, so maybe it's not named #pragma pack
.
"Normally", there is no reason to interfere with structure packing, but one reason is sending data over a network or to a file. When the structs are packed with no padding at all, you can cast them to a void *
or char *
and pass the structs directly to a "send" function, for example:
send((void *)&data_to_send, sizeof(data_to_send));
The variable name data_to_send
in your question is a hint that this could be what happens in this code. I'm not saying this is good practice, but it's quite common, because you don't have to write serializing code.
Upvotes: 2
Reputation: 23218
How could I inform the compiler that you should skip the padding byte while reading the elements of structure ?
Short answer: You cannot.
The compiler will not dis-regard contents contained in your struct. However you can control how it will treat the contents in your struct.
I am curious to know why the compiler behaves like that ?
Short answer: data alignment.
Two issues to consider: data alignment boundaries and data structure padding. You have some control over each:
Data alignment Is the reason your compiler sees what it sees. Data alignment means putting the data at a memory address equal to some multiple of the word size (4 bytes for a 32 bit environment) Even if you do not use explicit padding, the data is stored such that these boundaries are observed, and the size of the struct will indicate padding in the total byte space used.
Structure padding - meaningless bytes placed into a structure to help align the size to be a multiple of word size. You have this in your example code.
You can use pragma macros that cause compiler to pre-process (resolve before compile) packing of a struct a certain way: example #pragma pack(n) simply sets the new alignment. Or, #pragma pack() sets the alignment to the one that was in effect when compilation started.
#pragma pack(push) /* push current alignment to stack */
#pragma pack(1) /* set alignment to 1 byte boundary */
struct MyPackedData
{
char Data1;
long Data2;
char Data3;
};
#pragma pack(pop) /* restore original alignment from stack */
Note: The unit of n for pack(n) is byte. Values for n are compiler specific, for MSVC for example are typically 1, 2, 4, 8, and 16.
Question: If you are using prama pack macros, do they use consistent pack values between the getter()/setter() functions? (credit to @alain)
But again, this will not cause the compiler to disregard the contents of your struct, only process it a different way.
See information here and here for more information on root cause of your observations.
Upvotes: 2