Search code examples
cmemcpy

Will memcpy-ing a struct to bytes, and back guarantee the same data even with padding? And interpreting bytes as struct?


A very simple example

#include <stdint.h>

typedef struct {
  uint8_t a;
  uint64_t b;
} Test;

// With no struct *packing* sizeof(Test) == 16 on my machine

// Create some data
uint8_t buf[sizeof(Test)];
Test test;
test.a = 255;
test.b = 1234567890;

memcpy(buf, &test, sizeof(Test));

// Now copy the data from buf into a new structure
Test test2;
memcpy(&test2, buf, sizeof(Test));

// Interpret the bytes as a struct Test
Test* test_ptr = (Test*)buf;

// If all of this is done on the same machine (no transmitting data across different sets of hardware)
// are we guaranteed to always have the following conditions
test.a == test2.a;
test.a == test_ptr->a;
test.b == test2.b;
test.b == test_ptr->b;

This works on my machine but I am just wondering if it just happens to be an instance of getting lucky with the compiler/architecture. Also what happens if there is a more complicated data structure where there is more padding in different places?

This is all happening on the same machine, so not worrying about sending buf over a serial/tcp/etc link and mis-interpreting the bytes.


Solution

  • Yes, it is guaranteed as the memory layout of the different objects having the same type has to be identical.

    typedef struct {
      uint8_t a;
      uint64_t b;
    } Test;
    
    Test t1, t2;
    uint8_t buf[sizeof(Test)];
    t1.a = 255;
    t2.b = 1234567890;
    
    memcpy(buf, &t1, sizeof(t1));
    
    memcpy(&t2, buf, sizeof(t2));
    

    t2 & t1 will have identical memory footprint.

    This is all happening on the same machine, so not worrying about sending buf over a serial/tcp/etc link and mis-interpreting the bytes.

    If on the both sides of the link you will have the same program (ie compiled using the same compiler & compile options) it will be safe.

    Otherwise it is not safe and data has to be serialized.

    PS. I have missed one thing:

    // Interpret the bytes as a struct Test
    Test* test_ptr = (Test*)buf;
    

    it is pointer pointer punning and it is an UB. Use memcpy or union punning instead.

    Do not be afraid of memcpy as compiler will in most cases optimize it out and this assignment or punning will be done very efficient way.