user81368: I am logging binary data to my SD. I log a datetime object of type 'tm', which is 36 bytes on ESP32.
When I create a datetime of type 'tm' on linux it is 56 bytes.
ESP32 write tm to SD:
[tm datetime = 36 bytes]
time_t t = std::time(nullptr);
tm datetime = *std::localtime(&t);
uint16_t t = file.write((const uint8_t *)&datetime, sizeof(datetime)); // ==36 bytes.

PC
[tm datetime = 56 bytes] (compiled using g++ on linux 64)
tm datetime;
std::ifstream fileStream(filename, std::ios::binary);
fileStream.read((char *)&datetime, sizeof(datetime));

tm is defined in time.h:
struct tm
{
  int   tm_sec;
  int   tm_min;
  int   tm_hour;
  int   tm_mday;
  int   tm_mon;
  int   tm_year;
  int   tm_wday;
  int   tm_yday;
  int   tm_isdst;
#ifdef __TM_GMTOFF
  long  __TM_GMTOFF;
#endif
#ifdef __TM_ZONE
  const char *__TM_ZONE;
#endif
};

My tm struct doesn't have the 'tm_gmtoff' or 'tm_zone', even if it had it wont add up to 56 bytes.
How can my tm be 56 bytes on my PC?
If someone has a better way to log a datetime object, without this issue, let me know.

user4143: You can not trust system-level structs for data storage or transfer between different systems. The main reasons are:

Different data type sizes (16 / 32 / 64 bit ints, floats vs doubles, etc)
Struct element alignment differences owing to underlying architecture optimizations and limitations

Unless you are working with a system struct that is specifically designed for cross-system communication you must define your own and pay special attention to the layout.

Always use explicitly sized variable types (uint8_t, int32_t, etc for example) to ensure both ends always agree on data storage sizes
Always "pack" your struct (__attribute__((packed))) to collapse any specially aligned variables into just the room needed to store those variables

Point 2 requires special attention if you're doing much more than filling the struct and saving / transmitting it. It's advisable to never do more than create the struct instance, fill it, save it, then throw it away. If you start doing advanced operations with pointers to the struct you risk corrupting the packing code if the alignment changes. Also packed structs are often slightly slower to access owing to more complex code usually needed to be used to read / write the individual fields within them (especially if there are no byte-wise operations in the underlying architecture).

81368: <p>I am logging binary data to my SD. I log a datetime object of type 'tm', which is 36 bytes on ESP32.
When I create a datetime of type 'tm' on linux it is 56 bytes.</p>
<p>ESP32 write tm to SD:
[tm datetime = 36 bytes]</p>
<pre><code>time_t t = std::time(nullptr);
tm datetime = *std::localtime(&amp;t);
uint16_t t = file.write((const uint8_t *)&amp;datetime, sizeof(datetime)); // ==36 bytes.
</code></pre>
<p>PC
[tm datetime = 56 bytes] (compiled using g++ on linux 64)</p>
<pre><code>tm datetime;
std::ifstream fileStream(filename, std::ios::binary);
fileStream.read((char *)&amp;datetime, sizeof(datetime));
</code></pre>
<p>tm is defined in time.h:</p>
<pre><code>struct tm
{
  int   tm_sec;
  int   tm_min;
  int   tm_hour;
  int   tm_mday;
  int   tm_mon;
  int   tm_year;
  int   tm_wday;
  int   tm_yday;
  int   tm_isdst;
#ifdef __TM_GMTOFF
  long  __TM_GMTOFF;
#endif
#ifdef __TM_ZONE
  const char *__TM_ZONE;
#endif
};
</code></pre>
<p>My tm struct doesn't have the <a href="https://stackoverflow.com/questions/13969210/size-of-struct-tm">'tm_gmtoff' or 'tm_zone'</a>, even if it had it wont add up to 56 bytes.</p>
<p>How can my tm be 56 bytes on my PC?
If someone has a better way to log a datetime object, without this issue, let me know.</p>
 4143: <p>You can not trust system-level structs for data storage or transfer between different systems. The main reasons are:</p>
<ol>
<li>Different data type sizes (16 / 32 / 64 bit ints, floats vs doubles, etc)</li>
<li>Struct element alignment differences owing to underlying architecture optimizations and limitations</li>
</ol>
<p>Unless you are working with a system struct that is specifically designed for cross-system communication you must define your own and pay special attention to the layout.</p>
<ol>
<li>Always use explicitly sized variable types (uint8_t, int32_t, etc for example) to ensure both ends always agree on data storage sizes</li>
<li>Always &quot;pack&quot; your struct (<code>__attribute__((packed))</code>) to collapse any specially aligned variables into just the room needed to store those variables</li>
</ol>
<p>Point 2 requires special attention if you're doing much more than filling the struct and saving / transmitting it. It's advisable to never do more than create the struct instance, fill it, save it, then throw it away. If you start doing advanced operations with pointers to the struct you risk corrupting the packing code if the alignment changes. Also packed structs are often slightly slower to access owing to more complex code usually needed to be used to read / write the individual fields within them (especially if there are no byte-wise operations in the underlying architecture).</p>
