March 2022
A great article. For an optimized and/or secure system, it’s important to pay attention to these small details of how things look in memory.
However, I can’t believe I didn’t know about the pahole
tool - I’ve been manually inserting those offset comments when optimizing…
I recently wrote an article about a similar topic - and one thing I wrote about is what unaligned accesses look like in memory and why they are bad or simply not allowed:
http://www.shincbm.com/embedded/2022/02/18/struct-layout-code-size.html
March 2022
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;
}
March 2022
This has always seemed like a common usage pattern. However, I see a lot of articles on this topic lately.
A great article. 