Trying to understand why sizeof(structure) doesn't match expectation

Status
Not open for further replies.

Talkiet

Well-known member
I have a structure I need to send across LORA so I am trying to create a CRC of the unioned char array but I am having a strange time understanding the layout of the structure.

This is the structure in question

Code:
struct timepacket { // 36 bytes according to sizeof()
  byte startorfinish; // 1
  int carnum;         // 4
  time_t pulsetime;   // 4
  uint32_t pulsems;   // 4
  byte sats;          // 1
  int millivolts;     // 4
  boolean sent;       // 1
  int crc;            // 4
  byte terminator;    // 1
};

And this is the union

Code:
union outputLora {
  timepacket timepacketdata;
  byte LoraLine[timepacketsize];
};

sizeof() tells me the structure is 36 bytes but my awful math says 28 bytes

By putting known values into the array and looking at the char union byte by byte, I think I have this...

Code:
timepacket structure
                           1                   2                   3
         1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
startfin X
carnum     X X X X
pulsetime          ? ? ? X X X X
pulsems                          X X X X
sats                                     X
mv                                         ? ? ? X X X X
sent                                                     X
crc                                                        X X X X
term                                                               X

Acknowledging that it doesn't solve everything - pulsetime and mv don't seem to start where I expect (The ? in the table represent what look like dead bytes that aren't used - is this an artifact of those values needing to start on a certain byte boundary? Would I make a couple of bytes saving by stacking the byte and boolean values together in the structure?

This is doing my head in!

Cheers - N
 
32 bit integers have to be 4-aligned in memory, so that accessing them doesn't bus-error, so mixing bytes
and ints will waste a lot of space. The overall size is rounded to a multiple of 4 so that sizeof(array_of_struct)/sizeof(struct)
works. The machine architecture determines what the alignment rules are - its not the same on all
processors.
 
Thanks for the instant response, with that extra insight it all makes sense now and I can sleep :)

Cheers - N
 
Remember that integers are saved in little endian , least significant byte first.

This is what I get, Teensy 3.0
Code:
union outputLora data;
data.timepacketdata.startorfinish = 255;
data.timepacketdata.carnum = 1;
data.timepacketdata.pulsetime = 0xFFFFFFF0;
data.timepacketdata.pulsems = 0xA0A0A0A0;
data.timepacketdata.sats = 0x3;
data.timepacketdata.millivolts = 4;
data.timepacketdata.sent = true;
data.timepacketdata.crc = 0xC0C0C0C0;
data.timepacketdata.terminator = 255;

Printing the byte array in HEX with one space between bytes and two space at 4 byte boundaries:
Code:
FF 0 0 81  1 0 0 0  F0 FF FF FF  A0 A0 A0 A0  3 AA AA AA  4 0 0 0  1 ED 0 E0  C0 C0 C0 C0  FF E0 3 40  
   X X X                                        X  X  X               X X X                    X X X

X marks padding, so all 4 byte data like int and uint32_t are aligned at 4 byte boundaries.
 
If you have cases where you really do need that data to set setup like to have that data stored in 28 bytes, there are some ways to do so...

You can tell the system to pack the structure: There are multiple ways to do so.

For example you can use the older style: #pragma pack
Where for example you could do something like:
Code:
#pragma pack(push,1)
struct timepacket { // 36 bytes according to sizeof()
  byte startorfinish; // 1
  int carnum;         // 4
  time_t pulsetime;   // 4
  uint32_t pulsems;   // 4
  byte sats;          // 1
  int millivolts;     // 4
  boolean sent;       // 1
  int crc;            // 4
  byte terminator;    // 1
};
#pragma pack(pop)
Which will tell the system to pack it at byte boundary and the push part says remember what the setting was, and the pop says restore it...
But warning, this can lead to issues, like on some processors or certain conditions, variables need to be aligned on their natural alignment or the processor can crash.

Or more current you could use the attribute way like DMA does:
Code:
	typedef struct __attribute__((packed, aligned(4))) {
		volatile const void * volatile SADDR;
		int16_t SOFF;
		union { uint16_t ATTR;
			struct { uint8_t ATTR_DST; uint8_t ATTR_SRC; }; };
		union { uint32_t NBYTES; uint32_t NBYTES_MLNO;
			uint32_t NBYTES_MLOFFNO; uint32_t NBYTES_MLOFFYES; };
		int32_t SLAST;
		volatile void * volatile DADDR;
		int16_t DOFF;
		union { volatile uint16_t CITER;
			volatile uint16_t CITER_ELINKYES; volatile uint16_t CITER_ELINKNO; };
		int32_t DLASTSGA;
		volatile uint16_t CSR;
		union { volatile uint16_t BITER;
			volatile uint16_t BITER_ELINKYES; volatile uint16_t BITER_ELINKNO; };
	} TCD_t;
Note in the above, the packed says to pack everything, but when you create a variable of this type, align it to 4 byte boundary.

Or if you don't really care what order the variables are in your structure, you can simply rearrange all of the variables in your structure to align them. One simple way is put them all in the structure from largest to smallest.
Or other times you can do it carefully by grouping them correctly.
 
Well I am transmitting the data across a LORA network (some ebyte DTUs including some acting as repeaters) so I want the best range/highest spreading factor and that means lowest speed - I'll probably end up at 300bps air data rate. With the equipment I am using I can specify the size of the packet they will send and one option is 32 bytes - so I am working to get the structure below 32 bytes including CRC... That was the main reason for getting upset at 36 bytes sizeof() when I knew there wasn't that much data in there.

I was also needing to determine the location of the CRC in the structure as I need to exclude those bytes from the calculation.

With the information in the thread I have optimised the structure in approximately the way you suggest. I moved the CRC to the first value so instead of worrying about the offset it's always zero, and then put all the byte and boolean values together and then all the 32 bit values. It now fits under 32bytes, I know where the CRC will be, and more importantly, I learned something today :)

Cheers- N
 
Status
Not open for further replies.
Back
Top