That’s some great data, thanks Noah! Sound conclusions too. I think in C it pays to be explicit.
A potentially useful technique to enable this practice: union
with a byte array. In some scenarios (eg. serialisation) I’ve found this useful to have a convenient record based syntax as well as contiguous byte access so you don’t have to rely on implicit assumptions.
For example:
struct foo {
uint32_t i;
uint8_t b;
};
union fooWithByteAccess {
foo fields;
uint8_t bytes[sizeof(foo)];
}
You can then avoid memcpy and use an initialiser instead:
union fooWithByteAccess f = {.bytes = {0}};
You still need to be very careful - reading and writing to a union using different members is called “type punning”, and if the members are different sizes, or you’re using C90, there are really nasty dragons. See here for some confusing clarifications - as always, trust but verify!
A working example:
#include <stdio.h>
#include <stdint.h>
struct foo {
uint32_t i;
uint8_t b;
};
union fooWithByteAccess {
struct foo fields;
uint8_t bytes[sizeof(struct foo)];
};
int main(int argc, char *argv[])
{
union fooWithByteAccess f = {.bytes = {0}};
f.fields.i = 1234;
f.fields.b = 123;
printf("sizeof(f) = %lu\n", sizeof(f));
printf("f.bytes[0] = 0x%02X\n", f.bytes[0]);
printf("f.bytes[1] = 0x%02X\n", f.bytes[1]);
printf("f.bytes[2] = 0x%02X\n", f.bytes[2]);
printf("f.bytes[3] = 0x%02X\n", f.bytes[3]);
printf("f.bytes[4] = 0x%02X\n", f.bytes[4]);
printf("f.bytes[5] = 0x%02X\n", f.bytes[5]);
printf("f.bytes[6] = 0x%02X\n", f.bytes[6]);
printf("f.bytes[7] = 0x%02X\n", f.bytes[7]);
return 0;
}