简体   繁体   中英

Serialize struct to byte array with pointer as struct member

I am trying to copy a byte array to my struct, then serialize my struct to a byte array again.

But, after I serialize my struct array, I cant get my data value (0x12, 0x34, 0x56) again, instead i get some rubbish data.

What is wrong here?

#pragma pack(push, 1)
typedef struct {
    uint8_t length;
    uint8_t *data;
} Tx_Packet;
#pragma pack(pop)

static void create_tx_packet(uint8_t *packet, uint8_t *src, int length);

int main(void)
{
    uint8_t packet[32];
    uint8_t data[] = { 0x12, 0x34, 0x56 };

    create_tx_packet(packet, data, 3);

    //i check using debugger, i cant get the data value correctly
    //but i could get length value correctly


    return 0;
}

static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
    Tx_Packet *tx_packet = malloc(sizeof(*tx_packet ));

    tx_packet->length = length;
    tx_packet->data = (uint8_t *)malloc(length);
    memcpy(tx_packet->data, src, length);

    memcpy(packet, tx_packet, sizeof(*tx_packet));
}

Right now, your create_tx_packet() function copies a Tx_Packet struct created in the function to a uint8_t array. That struct contains the length and a pointer to the data, but not the data itself. It's actually not necessary to use the struct as an intermediate step at all, particularly for such a simple packet, so you could instead do:

static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
  *packet = length; /* set (first) uint8_t pointed to by packet to the
                       length */
  memcpy(packet + 1, src, length);  /* copy length bytes from src to
                                       the 2nd and subsequent bytes of
                                       packet */
}

You still need to make sure packet points to enough space (at least length + 1 bytes) for everything (which it does). Since the version above doesn't dynamically allocate anything, it also fixes the memory leaks in your original (which should have freed tx_packet->data and tx_packet before exiting).

--

If you do want to use a struct, you can (since the data is at the end) change your struct to use an array instead of a pointer for data -- then extra space past the size of the struct can be used for the data, and accessed through the data array in the struct. The struct might be:

typedef struct {
  uint8_t length;
  uint8_t data[];
} Tx_Packet;

and the function becomes (if a temporary struct is used):

static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
  /* allocate the temporary struct, with extra space at the end for the
     data */
  Tx_Packet *tx_packet = malloc(sizeof(Tx_Packet)+length);

  /* fill the struct (set length, copy data from src) */
  tx_packet->length = length;
  memcpy(tx_packet->data, src, length);

  /* copy the struct and following data to the output array */
  memcpy(packet, tx_packet, sizeof(Tx_Packet) + length);

  /* and remember to free our temporary struct/data */
  free(tx_packet);
}

Rather than allocate a temporary struct, though, you could also use struct pointer to access the byte array in packet directly and avoid the extra memory allocation:

static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
  /* Set a Tx_Packet pointer to point at the output array */
  Tx_Packet *tx_packet = (Tx_Packet *)packet;

  /* Fill out the struct as before, but this time directly into the
     output array so we don't need to allocate and copy so much */
  tx_packet->length = length;
  memcpy(tx_packet->data, src, length);
}

If you use memcpy(packet, tx_packet, sizeof(*tx_packet)); you are copying the memory representation of tx_Packet into packet, starting with tx_packet->length .

Additionally when mallocating tx_packet that size should be sizeof(*packet)+sizeof(uint8_t) (length of packet plus length field)

And again when copying the tx_packet back to packet you are writing out of the boundaries of packet .

EDIT:
I forgot to mention that depending on your compiler memory alignment parameter you could get any length for the fields (including tx_packet->length ) to accelerate memory operation. On 32bits machine it could be 4 and padded with rubbish.

When you serialize your struct with

memcpy(packet, tx_packet, sizeof(*tx_packet));

you're copying the length and the pointer to the data, but not the data itself. You'll probably need two memcpy calls: one of sizeof(uint8_t) to copy the length field, and one of length to copy the data.

This line:

Tx_Packet *tx_packet = malloc(sizeof(*packet));

only allocates one byte for the packet header, which you then immediately write off the end of, causing undefined behavior. You probably meant

Tx_Packet *tx_packet = malloc(sizeof(*tx_packet));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM