In a project I would like to save some data to EEPROM of a classic Nano.
While developing I encountered that the size of the below TIMEOUT struct (23 bits) is 3 bytes (which suites me well so I possibly can squeeze a bit more out) but I would have expected it to be four bytes due to the uint32_t.
Was my expectation wrong? Any explanation (I know that compilers are quite clever)?
I'm going to guess that, since an 8-bit processor can efficiently access byte addresses, that the compiler is allowed to assign the smallest integral number of bytes needed to hold the structure.
Bet it would have used 4 bytes on an ARM or ESP since they likely access 32-bit addresses more efficiently. I'll check and let you know.
The datatype (uint32_t) just has to be large enough to accommodate the number of bits. For example, startTime cannot be a uint8_t.
I suspect the compiler promotes to the datatype specified (I cannot find a reference). In other words, the compiler treats quarter as a uint32_t but stores the value in 3 bits. If that's true you can save some space / code by choosing the smallest datatype that can store the number of bits.
No. But for a different reason. The TIMEOUT struct is subject to packing / padding like any other struct. Without telling the compiler you want a packed struct the compiler can pad. In the extreme case your struct could become 12 bytes in size. (At least that's my reading of the documentation.)
/home/wim/Downloads/arduino-1.8.19/portable/sketchbook/Netball/NetballTimer.0.9/fsmMain.cpp:442:132: warning: format '%d' expects argument of type 'int', but argument 3 has type 'long unsigned int' [-Wformat=]
sprintf(buffer, "%d>%02d:%02d %02d:%02d", to.quarter, to.startTime / 60, to.startTime % 60, to.duration / 60, to.duration % 60);
A little strange (for me) that it only complains about the quarter; after I fixed that using a cast to uint8_t no further warnings showed.
I'm aware that packing/padding exists. Just for fun, is there a flag (attribute) to tell the compiler not to pack.
Microsoft's compiler has a lot of options. I'm not certain about GCC. Normally, a compiler packs / aligns to what works best for the target platform. In the case of an AVR processor that would essentially be "not applicable" for both (byte-aligned, byte-packing).
Let's find out...
This appears to be the GCC way. The options are packed and aligned. There is an option for "natural" alignment; do not include the argument...
struct S { short f[3]; } __attribute__ ((aligned));