0

I'm receiving byte buffer array and trying to copy it to a structure:

my structure is:

typedef struct mydata_block
{
    uint8_t cmd;
    uint32_t param;
    char str_buf[10];
    uint32_t crc32;
} mydata_t;

first, the program that sends the data as following:

blockTX.cmd = 2

blockTX.str_buf = "eee789"

blockTX.param = 1001

blockTX.crc32 = 3494074521

-

02-00-00-00-E9-03-00-00-65-65-65-37-38-39-00-00-00-00-00-00-99-58-43-D0

when the data is recieved im copying the data to the structure using the memcpy code below:

    memcpy((uint8_t *)&blockRX,(uint8_t *)usbd_cdc_buffer,sizeof(blockRX));

everything looks fine, but not the cmd (its 1 byte but there is padding? in structure?) how do i fix this?

Hasan alattar
  • 347
  • 3
  • 19
  • 9
    Do not use memcpy. You need to parse the byte stream, "understand" the value individually and write them explicitly and individually to the members of the struct. Any assumption on padding, sizes, endianess etc. will make your program non-portable and prone to fail in the future. – Yunnosch Jul 19 '18 at 07:09
  • 1
    There might be padding it is compiler specific. I'm concerned about the mixture of types, for example `blockTX.str_buf = "eee789";` is not the way to copy a string, you should use `strcpy`. – cdarke Jul 19 '18 at 07:10
  • @cdarke well, sorry but that blockTX code was not a c code its in c# in pc.. and it was actually: blockTX.str_buf = Encoding.ASCII.GetBytes("eee789"); but i striped it just to show the data not the code. – Hasan alattar Jul 19 '18 at 07:11
  • @Yunnosch wouldnt parsing byte stream make longer code and memory/processing? + when i change the structure in the future i have to go edit the function of byte stream? – Hasan alattar Jul 19 '18 at 07:12
  • Then please add a comment in your question that the block is pseudo code. Does the C# code expand the string to 10 bytes, zero delimited? – cdarke Jul 19 '18 at 07:17
  • 7
    Longer, slower yes. But long slow correct is better than short fast wrong or short fast non-reusable. Same goes with the need to change the code to understand new message structure. – Yunnosch Jul 19 '18 at 07:18
  • Possible duplicate of [Access struct as array of bytes](https://stackoverflow.com/questions/28070273/access-struct-as-array-of-bytes) – bobby Jul 19 '18 at 07:24
  • @Yunnosch *Longer, slower yes.* I'd say "Longer, slower **maybe**". A good optimizing compiler might make filling in a `struct` element-by-element faster than having to do a full function call to `memcpy()`. And either way, the translation from a byte array to a `struct` is likely to be at least a few orders of magnitude faster than reading the data from its source into memory. Otherwise, absolutely right. It doesn't matter how fast you get bad answers. – Andrew Henle Jul 19 '18 at 11:55
  • 1
    @AndrewHenle Good point. Lets compromise on "**Even if** longer slower." ;-) – Yunnosch Jul 19 '18 at 14:35
  • @Hasanalattar: Leave optimisations to the compiler **until you have proof it is an issue**. In that case profile and optimise hot-spots. Until then concentrate onm writing **readable** and correct code. (oh, and your question lacks required information, read [ask]) – too honest for this site Jul 19 '18 at 15:57
  • In general not a good idea to use structs across compile domains. How you fix it is by not using structs across compile domains, but something more consistent and reliable. An array of bytes or words. Use endian/size safe code to fill in the array before sending and extract on the way out. You can re-use that code on both sides. – old_timer Jul 22 '18 at 02:02
  • If you insist on trying to use structs, and deal with maintenance down the road. Start the struct with the largest items and make them aligned, even if you dont need them for example make the 10 byte array 16 bytes. then the 32 bit items then the one byte item, then you can pack it and have a better chance of success, and less chance of an alignment fault. – old_timer Jul 22 '18 at 02:06

1 Answers1

4

Transfering data needs to consider padding, sizes, endianess etc so you need to generate and parse the byte stream correctly. You can use something like googloe protobuf to serialize and deserialize your data protable and comfortable.

But if you must you can give the structure the packed attribute. This removes all the padding and alignment restrictions. That lets you memcpy() the struct without paddings but at the cost of slower access to the members of the struct itself. There are only two good reasons to do this:

  1. The alignemnt and padding of the struct is determined by forces outside your control (has to match hardware or 3rd party software).
  2. As intermediate step to converting the data into host format.
Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42
  • Also, packing a structure can result in code that doesn't run. It gets used a lot because x86 allows it. Other hardware exists that doesn't allow unfettered access to misaligned data. – Andrew Henle Jul 19 '18 at 11:51
  • If packing creates code that doesn't run then your compiler is broken. The packed attribute tells the compiler that no padding or alignment is used for the struct. That also means access to any member of the struct has to handle unaligned access. For example on ARM reading a `struct { int x; } __attribute((packed))` results in 4 separate byte reads, shifts and ors. Hence the much slower part. – Goswin von Brederlow Jul 19 '18 at 15:39
  • [So GCC is broken?](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51628) `#pragma pack` is playing with fire on many non-x86 platforms. – Andrew Henle Jul 19 '18 at 20:26
  • Note that you still have the issue of endianness. – starblue Jul 20 '18 at 08:05
  • @AndrewHenle Nothing in there about structure access being broken. When taking the address of a members of a packed struct you have to always make a pointer to a packed struct too, e.g. `typedef struct { int32_t unaligned_int32; } __attribute((packed)) unaliged_uint32p;` – Goswin von Brederlow Jul 23 '18 at 10:54