2

Consider the following structs:

typedef struct {
  uint32_t foo;
  uint32_t bar;
} first_struct_t;

typedef struct {
  first_struct_t f;
  uint8_t *p;
  uint8_t buf[];
} second_struct_t;

However, later in my code, the following assignment happens:

typedef struct {
  first_struct_t *f;
  // ...
} some_struct;

int function_before_foo(some_struct *p) {
  p->f = (second_struct_t *) malloc(sizeof(second_struct_t));
  // ...
}

int function_foo(some_struct *p) {
  second_struct_t *s = (second_struct_t *) p->f;
  // ...
}

And it generates the following error due to -Wcast-align:

'second_struct_t *' increases required alignment from 4 to 8 [-Werror,-Wcast-align]
second_struct_t *s = (second_struct_t *) p->f;

A solution is to cast it to a void *, but that seems to only mask the problem. What is the cleanest solution to this?

EDIT: This only happens with clang and not GCC, regardless of the fact that both compilers are given the -Wcast-align flag.

MarkP
  • 4,168
  • 10
  • 43
  • 84

1 Answers1

1

I'm going to assume when you have struct_second_t you actually meant second_struct_t, otherwise your code does not compile.

The problem is that f in some_struct is a pointer to a first_struct_t not a pointer to a second_struct_t.

I should add that the cast in struct_second_t *s = (struct_second_t *) p->f; is hiding the warning message. In general if you have to cast one pointer to another you are more often than not going to cause undefined behavior. This is not always true but is a pretty good guideline.

In response to comment.

First it appears you will not get that warning with gcc for x86 (32 and 64 bit) as there is no alignment requirement for general purpose registers although alignment can improve performance (see this SO post for more info). As to why clang emits that warning, perhaps because of performance or they do not have the exception as gcc does for x86.

Second what you are trying to accomplish is similar to what the container_of macro does in the Linux kernel. container_of is typically defined as:

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

In searching the only thing I have found that addresses your issue is this commit to tup that changed their version of container_of to include a cast to void*.

Basically, I believe the issue is while you know that p->f actually points to a second_struct_t your compiler does not and therefore issues the warning. So you can either not do that or cast to void*.

Additional:

It seems mozilla address the issue by casting to void* as well:

Finally, I would recommend looking at container_of and using that instead of relying on the fact that the first element of the struct is the other struct. This way your code is more resilient, if someone else changes the order of the struct members it will still work. You will have to add a void* cast to container_of to avoid the warning. Note that on architectures with alignment issues, you should not have an issue at runtime assuming you always do your type change between child and parent correctly.

Community
  • 1
  • 1
missimer
  • 4,022
  • 1
  • 19
  • 33
  • I'm using the `->f` variable to store a pointer to an instance of `second_struct_t`. Basically it's a cheap way of doing polymorphism. You can use `->f` to access `first_struct_t` and if you know about it, you can gain access to `second_struct_t`. In this case, I know about `second_struct_t` and am willing to cast it that way. – MarkP Oct 19 '15 at 16:45