1

I have some c++ code that defines a struct:

struct IcmpHdr
{
    uint8_t m_type;
    uint8_t m_code;
    uint16_t m_chksum;
    uint16_t m_id;
    uint16_t m_seq;
} __attribute__((packed, aligned(2)))

I understand that this struct will always be aligned on an address divisible by 2 when allocated because a padding byte ahead of the struct will be added if necessary.

This struct gets cast to a byte array before going over the wire to be unpacked on the the receiving end. Now what happens on the receiving end if I store the bytes in an array char byte_array[8];

And then ultimately cast this as a pointer to my type?

IcmpHdr* header = (IcmpHdr*)byte_array;

Will the struct have a 50/50 chance of being misaligned? Could this cause undefined behavior when dereferencing the members? Other issues?

I know I could just align the array on a 2 byte boundary to avoid even having to think about this. Curiosity is my main reason for asking.

  • 2
    You'd have some chance of being misaligned **and a 100% chance of a strict aliasing violation and therefore undefined behavior**. – Andrew Henle Dec 03 '21 at 16:08
  • 2
    Don’t, just don’t. There is no reason to attempt to make bad code work when there is a supported way to write good code. When receiving a buffer like this, define an `IcmpHdr` object and read the data into it (pass it as the buffer to the network call that writes the received data into a buffer). In cases where you are reading a packet and do not know which type it is until after starting to inspect it, you can use a union of the various packet types. Another option is to read into a character buffer and then `memcpy` into a proper `IcmpHdr` object. – Eric Postpischil Dec 03 '21 at 16:13
  • 1
    The aliasing rule Andrew Henle aludes to is basically: Do not lie to the compiler about what type an object is. Modern compilers make a variety of assumptions about code based on object types. Even if the pointer is aligned as needed for an `IcmpHdr`, accessing an array of `char` using an lvalue of type `IcmpHdr` can result in the compiler generating code that does not do what you want. – Eric Postpischil Dec 03 '21 at 16:16
  • 1
    Have a look at std::bit_cast and this link : https://stackoverflow.com/questions/58320316/stdbit-cast-with-stdarray. ("C" style casts for "reinterpreting" data are UB anyway) – Pepijn Kramer Dec 03 '21 at 16:23
  • Do not tag both C and C++ except when asking about differences or interactions between the two languages. The aliasing rules are different in the two languages, and answers for one will not serve people who are looking for information about the other language. – Eric Postpischil Dec 03 '21 at 16:24
  • @EricPostpischil Apologies for saying this was c -- I was not aware of the difference and assumed the same would apply to both languages. I changed the question to mention c++ since that's what I really care about. – Nicholas Weiland Dec 03 '21 at 16:29

1 Answers1

0
  1. Avoid pointer punning as it almost always breaks strict aliasing rules.
  2. Alignment of your structure does not matter as your byte array does not have to be 2 bytes aligned.

Use memcpy

IcmpHdr header;
memcpy(&header, byte_array, sizeof(header));

If you use modern optimizing compiler it is very unlikely memcpy to be called.

https://godbolt.org/z/6P5M333dv

0___________
  • 60,014
  • 4
  • 34
  • 74