I'm trying to implement a polymorphic data structure e.g. an intrusive linked list (I already know the kernel has one - this is more of learning experience).
The trouble is casting a nested struct to the containing struct leads to gcc issuing a memory-alignment warning.
The specifics are provided below:
// t.c
#include <stdio.h>
enum OL_TYPE { A, B, C };
struct base {
enum OL_TYPE type;
};
struct overlay {
struct base base;
int i;
};
struct overlay2 {
struct base base;
float f;
int i;
// double d; // --> adding this causes a memory alignment warning
};
void testf(struct base *base) {
if (base->type == A) {
struct overlay *olptr = (struct overlay *)base;
printf("overlay->i = %d\n", olptr->i);
} else
if (base->type == B) {
struct overlay2 *olptr = (struct overlay2 *)base;
printf("overlay->i = %d\n", olptr->i);
}
}
int main(int argc, char *argv[]) {
struct overlay ol;
ol.base.type = A;
ol.i = 3;
testf(&ol.base);
}
Compiled with gcc t.c -std=c99 -pedantic -fstrict-aliasing -Wcast-align=strict -O3 -o q leads to this:
t.c: In function ‘testf’:
t.c:28:34: warning: cast increases required alignment of target type [-Wcast-align]
28 | struct overlay2 *olptr = (struct overlay2 *)base;
| ^
It's interesting to notice that if I comment out the double from overlay2 such that it's not part of the structure anymore, the warning disappears.
In light of this, I have a few questions:
- Is there any danger to this?
- If yes, what is the danger? If not, does that mean the warning is a false positive and how can it be dealt with?
- Why does adding the double suddenly lead to the alignment warning?
- Is misalignment / an alignment problem even possible if I cast from base to a
struct overlay2 *IF the actual type IS in fact astruct overlay2with a nestedstruct base? Please explain!
Appreciate any answers that can provide some insight
Only if what
basepoints to was not allocated as astruct overlay2in the first place. If you're just smuggling in a pointer tostruct overlay2as astruct base*, and casting back internally, that should be fine (the address would actually be aligned correctly in the first place).The danger occurs when you allocate something as something other than
struct overlay2then try to use the unaligneddoubleinside it. For example, a union ofstruct baseandchar data[sizeof(struct overlay2)]would be the right size to contain astruct overlay2, but it might be four byte aligned but not eight byte aligned, causing thedouble(size 8, alignment 8) to be misaligned. On x86 systems, a misaligned field typically just slows your code, but on non-x86 misaligned field access can crash your program.As for dealing with it, you can silence the warning (with some compiler-specific directives), or you can just force all your structures to be aligned identically, by adding
alignasdirectives (available since C11/C++11):Because it's the only data type in any of the structures that requires eight byte alignment; without it, the structures can all be legally aligned to four bytes; with it,
overlay2needs eight byte alignment while the others only need four byte alignment.As noted above, if it was really pointing to something allocated as a
struct overlay2all along, you're safe.