cgccmemory-alignmentstrict-aliasingintrusive-containers

Memory Alignment warning with gcc


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:

  1. Is there any danger to this?
  2. If yes, what is the danger? If not, does that mean the warning is a false positive and how can it be dealt with?
  3. Why does adding the double suddenly lead to the alignment warning?
  4. Is misalignment / an alignment problem even possible if I cast from base to a struct overlay2 * IF the actual type IS in fact a struct overlay2 with a nested struct base ? Please explain!

Appreciate any answers that can provide some insight


Solution

    1. is there any danger to this?

    Only if what base points to was not allocated as a struct overlay2 in the first place. If you're just smuggling in a pointer to struct overlay2 as a struct base*, and casting back internally, that should be fine (the address would actually be aligned correctly in the first place).

    1. If yes, what is the danger? If not, does that mean the warning is a false positive and how can it be dealt with?

    The danger occurs when you allocate something as something other than struct overlay2 then try to use the unaligned double inside it. For example, a union of struct base and char data[sizeof(struct overlay2)] would be the right size to contain a struct overlay2, but it might be four byte aligned but not eight byte aligned, causing the double (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 alignas directives (available since C11/C++11):

    #include <stdalign.h> // Add at top of file to get alignas macro
    
    struct base {
        alignas(double) enum OL_TYPE type;
    };
    
    1. why does adding the double suddenly lead to the alignment warning?

    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, overlay2 needs eight byte alignment while the others only need four byte alignment.

    1. is an alignment even possible if I cast from base to a struct overlay2 * IF the actual type IS in fact a struct overlay2 with a nested struct base ?

    As noted above, if it was really pointing to something allocated as a struct overlay2 all along, you're safe.