I have some existing C++ code that sends an receives an array of uint32_t over the network. Due to a change in my protocol, I want to replace each entry in this array with a pair of two uint16_ts, and if possible I would like to do this without changing the number of bits I send over the network. An obvious way to combine two uint16_t values into a single 32-bit-wide value is to do low-level bit-packing into a uint32_t, and leave the array definition unchanged. So the sender's code would look like this:
uint32_t items[ARR_SIZE];
for(std::size_t i = 0; i < ARR_SIZE; ++i) {
    //get uint16_t field1 and field2 somehow
    items[i] = static_cast<uint32_t>(static_cast<uint32_t>(field2) << 16)
                   | static_cast<uint32_t>(field1));
}
And the receiver's code would look like this:
//receive items
for(std::size_t i = 0; i < ARR_SIZE; ++i) {
    uint16_t field1 = static_cast<uint16_t>(items[i] & 0xffff);
    uint16_t field2 = static_cast<uint16_t>(items[i] >> 16);
    //do something with field1 and field2
}
However, this is ugly, type-unsafe, and relies on hard-coded magic numbers. I wonder if it is possible to accomplish the same thing by defining a 2-member struct that "should" be exactly the same size as a uint32_t:
struct data_item_t {
    uint16_t field1;
    uint16_t field2;
};
Then, the sender's code would look like this:
data_item_t items[ARR_SIZE];
for(std::size_t i = 0; i < SIZE; ++i) {
    //get uint16_t field1 and field2 somehow
    items[i] = {field1, field2};
}
And the receiver's code would look like this:
//receive items
for(std::size_t i = 0; i < ARR_SIZE; ++i) {
    uint16_t curr_field1 = items[i].field1;
    uint16_t curr_field2 = items[i].field2;
    //do something with field1 and field2
}
Will this work equivalently to the bit-packed uint32_ts? In other words, will the items array contain the same bits when I use struct data_item_t as when I use uint32_t and bit-packing? Based on the rules of structure padding, I think a struct containing two uint16_ts will never need any internal padding to be properly aligned. Or is that actually up to my compiler, and I need something like __attribute__((__packed__)) to guarantee it? 
 
     
     
    