Arduino (ESP32) - Struct-Size and output obfuscated

392 Views Asked by At

I try to convert a struct into a byte stream on an EPS32. Below my struct/union.

#include <Arduino.h>

typedef struct {
  uint16_t header;
  float pi;
  uint16_t crc;
} BlackBoxStruct;

typedef union {
  BlackBoxStruct data;
  uint8_t bytes [sizeof(BlackBoxStruct)];
} BBD; 

BBD bbd;
uint8_t size, size2, size3;
char buf[100];

void setup() {
  Serial.begin(115200);
  bbd.data.header=0xFEEF;
  bbd.data.pi = 1.234;
  bbd.data.crc=0x1122;

  size = sizeof(BlackBoxStruct);
  size2 = sizeof(bbd);
  size3 = sizeof(BBD);
  sprintf(buf, "\n\nArraySize: %d, StructSize: %d, BBD: %d", size, size2, size3);
  Serial.println(buf);
  for (uint8_t i=0; i < size; i++) {
    sprintf(buf, "byte %03d is %02x", i, bbd.bytes[i]);
    Serial.println(buf);
  }

}

void loop() {
  // put your main code here, to run repeatedly:

}

I'm still wondering about the output produced by the for-loop

ArraySize: 12, StructSize: 12, BBD: 12
byte 000 is ef <= header uint16_t 2 Bytes little endian 0xFEEF
byte 001 is fe
byte 002 is 00 <== ??? - seems to be a uint16_t or 2 uint8_t
byte 003 is 00
byte 004 is b6 <== float 4 bytes
byte 005 is f3
byte 006 is 9d
byte 007 is 3f
byte 008 is 22 <== CRC, uint16_t little endian 0x1122
byte 009 is 11
byte 010 is 00 <== ??? 2 bytes 
byte 011 is 00

I can't explain this 4 bytes marked with ???

Any idea?

I expected a size of the array of 8 Bytes the other 4 bytes - I don't know.

Much more obfuscated is this behaviour. I changed my struct and included two arrays.

typedef struct {
  uint16_t header;
  long ldata[2];
  float pid[2];
  uint16_t crc;
} BlackBoxStruct;

and the output

ArraySize: 24, StructSize: 24, BBD: 24
byte 000 is ef
byte 001 is fe
byte 002 is 00 <== long are 4 bytes - 1. LONG
byte 003 is 00 
byte 004 is 44 
byte 005 is 33 <== 0x3344
byte 006 is 00 <== long are 4 bytes - 2. LONG
byte 007 is 00  
byte 008 is cd 
byte 009 is ab <== 0xabcd
byte 010 is 00 <== ???
byte 011 is 00 <== ???
byte 012 is b6 <== 1. float
byte 013 is f3
byte 014 is 9d
byte 015 is 3f
byte 016 is 1b <== 2. float
byte 017 is 2f
byte 018 is 5d
byte 019 is 40
byte 020 is 22 <== crc 0x1122
byte 021 is 11
byte 022 is 00 <== ???
byte 023 is 00 <== ???
1

There are 1 best solutions below

2
hcheung On

I expected a size of the array of 8 Bytes the other 4 bytes - I don't know.

ESP32 has a data structure alignment of 4 as a 32-bit MCU, so even when you add all the elements within the struct based on its data type, it was supported to be 8 bytes, but padding were added to the both uint16_t elements to make it 4-byte long to be inline with the 4-byte float. The compiler do so for both performance reason as well as for data alignment purpose(see the links below for further reading). This can be observed with the following simple example.

#include <Arduino.h>

typedef struct{
  uint16_t head{0x1234};
  float f{1000.0};
  uint16_t tail{0x5678};
} Foo_t;

Foo_t foo;

void setup() {
  Serial.begin(115200);

  // since you are using an esp32, so Serial.printf() is available
  Serial.printf("The size of uint16_t head %d bytes\n", sizeof(foo.head));
  Serial.printf("The size of float f %d bytes\n", sizeof(foo.f));
  Serial.printf("The size of uint16_t tail %d bytes\n", sizeof(foo.tail));
  Serial.printf("The size of foo %d\n", sizeof(foo));

  uint8_t *ptr = (uint8_t*) &foo;
  for(int i=0; i<sizeof(foo); i++) {
      Serial.printf("byte %d=%02x\n", i, *ptr++);
  }

}

void loop() {

}

This will generate the output as:

The size of uint16_t head 2 bytes
The size of float f 4 bytes
The size of uint16_t tail 2 bytes
The size of foo 12
byte 0=34   // uint16_t 0x1234 stored in little endian
byte 1=12
byte 2=00   // padding for 0x1234
byte 3=00   // padding for 0x1234
byte 4=00   // float 1000.0 stored accoding to IEEE754 as 0x447a00000
byte 5=00
byte 6=7a
byte 7=44
byte 8=78   // uint16_t 0x5678 in little endian
byte 9=56
byte 10=00  // padding
byte 11=00  // padding

Now change the data struct to the following and see what do you get?

typedef struct{
  uint16_t head1{0x1234};
  uint16_t head2{0x5678};
  float f{1000.0};
  uint16_t tail{0x9abc};
} Foo_t;

or

typedef struct{
  uint8_t head1{0x12};
  uint16_t head2{0x5678};
  float f{1000.0};
  uint16_t tail{0x9abc};
} Foo_t;

All these struct will have the size of 12 bytes.

Further reading

Data Structure Alignment and C++ Struct size and data alignment.