Reputation: 351
I have a struct with a flexible array member, which I am using to store arbitrarily-sized data inline (for a generic linked list for any element type), and I can simply cast data
to T*
to access the data (it will only store one struct of T
). Obviously, I need to ensure that the field is aligned for any type T
. I do this via use of __attribute__((aligned))
and static_assert
, which shows me the field is clearly aligned.
I am having an issue where my compilers (GCC and Clang) do not seem to be correctly detecting this alignment, and are giving me cast alignment warnings with -Wcast-align=strict
.
#include <assert.h>
#include <stddef.h>
#include <stdalign.h>
// `data` is aligned to 16 bytes
struct S { int data_len; __attribute__((aligned(16))) char data[]; };
// validate the alignment is correct
static_assert((offsetof(struct S, data) % 16) == 0);
// struct is aligned to alignment of largest member, in this case `data`
static_assert(alignof(struct S) == 16);
// any type with a large required alignment
typedef long double big;
static_assert(alignof(big) > alignof(char));
big *f_s(struct S *s) {
// the `data` field is aligned to a multiple of the required alignment
// for a struct and a pointer of `big`
static_assert((offsetof(struct S, data) % alignof(big)) == 0);
static_assert((offsetof(struct S, data) % alignof(big*)) == 0);
return (big*)s->data;
}
Compilation with gcc input.c -Wcast-align=strict -c
:
input.c: In function ‘f_s’:
input.c:21:12: warning: cast increases required alignment of target type [-Wcast-align]
21 | return (big*)s->data;
| ^
Compilation with clang input.c -Wcast-align -Wno-c23-extensions -c
:
input.c:21:12: warning: cast from 'char *' to 'big *' (aka 'long double *') increases required alignment from 1 to 16 [-Wcast-align]
21 | return (big*)s->data;
| ^~~~~~~~~~~~~
Obviously this is wrong, as s->data
must be aligned more than big
, else the static_assert
would be throwing errors. Is there something that I am missing here, or is this a bug in GCC and Clang?
Upvotes: 2
Views: 124
Reputation: 154127
... as
s->data
must be aligned more thanbig
...
Is not quite so. Code (big*)s->data
is like (big*)(&(s->data[0]))
and that is converting a pointer to an object requiring 1 byte alignment (char *
) to a pointer to an object requiring more than 1 byte alignment (long double *
).
Instead, consider using max_align_t
for the FMA .data
type.
#include <assert.h>
#include <stddef.h>
#include <stdalign.h>
typedef long double big;
struct S_alt {
int char_data_size; // Consider `size_t char_data_size;`
max_align_t data[];
};
big* f_s_alt(struct S_alt *s) {
return (big*) s->data;
}
This may affect how .char_data_size
is derived and used in OP's unposted code, yet would need to see it to best advise how to handle.
Upvotes: 0