Struct size calculation

I am storing a struct in EEPROM and the expected size of the struct doesn’t match the stored size. In the example, I expect the struct to be 44 bytes. But reading back the data shows it is 48 bytes.

SYSTEM_MODE(MANUAL);

struct curveMetadata_t {
    uint8_t type;                       // (1 bytes) calibration type (family) indicator
    uint8_t numSets = 0;                // (1 bytes) number of stored curves
    float refPoints[5][2];              // (4*5*2 bytes) available reference pount values
    uint16_t address;                   // (2 bytes) EEPROM address of first curve in family
} cv; 

void setup() {
    Serial.begin(9600);
    delay(5000);
    EEPROM.clear();

    cv.type = 6;
    cv.numSets = 4;
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 2; j++) {
            cv.refPoints[i][j] = i+3;
        }
    }
    cv.address = 0x0508;
    EEPROM.put(58, cv);

    for (int c = 0; c < 50; c++) {
        Serial.println(EEPROM.read(58 + c));
    }
}

void loop() {
  
}

The results are shown below. Why are there 2 extra 00 bytes the beginning (06 04 00 00)? And why are there 2 extra bytes at the end (08 05 00 00 255 255). 255 is the empty EEPROM value.

Serial monitor opened successfully:
6
4
0
0
0
0
64
64
0
0
64
64
0
0
128
64
0
0
128
64
0
0
160
64
0
0
160
64
0
0
192
64
0
0
192
64
0
0
224
64
0
0
224
64
8
5
0
0
255
255

See Data structure alignment - Wikipedia for explanation, but the compiler is putting extra bytes of padding in for memory alignment.

Try packing your struct as follows which starts the float array on an even 4-byte boundary:

struct curveMetadata_t {
    uint16_t address;                   // (2 bytes) EEPROM address of first curve in family
    uint8_t type;                       // (1 bytes) calibration type (family) indicator
    uint8_t numSets = 0;                // (1 bytes) number of stored curves
    float refPoints[5][2];              // (4*5*2 bytes) available reference pount values
} cv; 
2 Likes

@laughlin Thank you for including the reference to the Wikipedia article. That has explained a few odd observations when storing and retrieving structs from eeprom. I have been padding the whole structure to a 4 byte multiple before adding an integer checksum - storing parameters to survive restart and be upgradable as well as include a checksum. Do you know whether this behaviour (compiler padding) has changed with the latest version of C compiler?

This is not a new behavior. It will differ based on platform however, and so the same struct may be padded differently when compiled for x86_64, vs PIC16, STM32, etc because different platforms can have different memory word sizes. Essentially, by keeping variables word-aligned in memory, the amount of machine code, and time required to access/modify said variables in memory are (generally) drastically reduced.

THanks. I’ll reorganize some of my structs.

Just as a side note, there is a compiler directive for packing structures:
https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_chr1384946440202.htm

In your case, you could use it this way to byte-pack your structure:

#pragma pack(push, 1)
struct curveMetadata_t {
    uint8_t type;                       // (1 bytes) calibration type (family) indicator
    uint8_t numSets = 0;                // (1 bytes) number of stored curves
    float refPoints[5][2];              // (4*5*2 bytes) available reference pount values
    uint16_t address;                   // (2 bytes) EEPROM address of first curve in family
} cv;
#pragma pack(pop)
3 Likes