Reputation: 51
Hi I'm trying to read a byte array into a struct and the bytes are coming out in the reverse order (to how i expected). Can someone help me understand whats happening please?
unsigned char buf[] = {
0x11, 0x22, 0x33, 0x44,
0x55, 0x66, 0x77, 0x88,
0x99, 0xaa, 0xbb, 0xcc
};
typedef struct mystruct {
uint16_t var1;
uint16_t var2;
uint32_t var3;
uint32_t var4;
} something;
int main(int argc,char **argv){
printf("sizeof buf: %lu %d \n",sizeof(buf),sizeof(something));
something *st = (something*)&(buf[0]);
#define pr(a) printf(#a" %x\n",a)
pr(st->var1);
pr(st->var2);
pr(st->var3);
pr(st->var4);
return(0);
}
Output:
sizeof buf: 12 12
st->var1 2211
st->var2 4433
st->var3 88776655
st->var4 ccbbaa99
I was expecting something like: st->var1 1122
Doing this also seems to output the same thing?
memcpy(&st->var1,buf,2);
pr(st->var1);
output: st->var1 2211
x86/Linux server, gcc version 4.5.3 (if that helps)
Thanks for your help.
Upvotes: 5
Views: 227
Reputation: 158459
As others have pointed out your main observable issue is that one of edianness.
Your method for accessing buf
is undefined behavior because it violates strict aliasing rules here by type punning here:
something *st = (something*)&(buf[0]);
If I built this code on using gcc
using the following arguments:
-O3 --Wstrict-aliasing=2
I receive the following warning:
main.cpp:22:4: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
something *st = (something*)&(buf[0]);
^
I am currently using version 4.8
. The relevant section from the C11 draft standard that covers aliasing rules is 6.5/7
.
Upvotes: 3
Reputation: 41017
As Joachim and Shafix have said, you can have problems with padding and endianness, but if you can make use of #pragma pack
this code should work (swap bytes when little endian)
#include <stdio.h>
#include <stdint.h>
unsigned char buf[] = {
0x11, 0x22, 0x33, 0x44,
0x55, 0x66, 0x77, 0x88,
0x99, 0xaa, 0xbb, 0xcc
};
typedef struct mystruct {
uint16_t var1;
uint16_t var2;
uint32_t var3;
uint32_t var4;
} something;
static uint16_t swap16(uint16_t val)
{
return ((val >> 8) & 0xFF) | ((val << 8) & 0xFF00);
}
static uint32_t swap32(uint32_t val)
{
uint16_t v1 = swap16((uint16_t) val);
uint16_t v2 = swap16((uint16_t) (val >> 16));
return (v1 << 16) | (v2);
}
int main(void)
{
printf("sizeof buf: %zu %zu \n", sizeof(buf), sizeof(something));
something *st = (something*)&(buf[0]);
#define pr(a) printf(#a" %x\n", a)
#if __BYTE_ORDER == __LITTLE_ENDIAN
st->var1 = swap16(st->var1);
st->var2 = swap16(st->var2);
st->var3 = swap32(st->var3);
st->var4 = swap32(st->var4);
#endif
pr(st->var1);
pr(st->var2);
pr(st->var3);
pr(st->var4);
return(0);
}
EDIT:
#pragma pack
will cause the pre-processor to discard the pre-determined alignment of the structure members and thus no padding bytes would be inserted, you can define your structure as follows:
#pragma pack(push, 1) // exact fit - no padding
typedef struct mystruct {
uint16_t var1;
uint16_t var2;
uint32_t var3;
uint32_t var4;
} something;
#pragma pack(pop) //back to whatever the previous packing mode was
Upvotes: 1
Reputation: 409166
If you read about endianness, you will see that there are two ways of storing data longer than one byte in memory.
For big-endian systems (like ARM), the integer value 0x1122
is stored in memory as (from lower to higher addresses) 0x11 0x22
. On a little-endian system (like x86) it's stored as 0x22 0x11
.
As your data in the array is stored "big-endian", you get the opposite byte ordering to what you expect on a little-endian system like yours.
Upvotes: 9