Reputation: 61
I have been looking into an existing C99 code base that uses pointers to packed structure members all over the place. This results in warnings of the form.
my_file.c:xxx:yy: error: taking address of packed member of 'struct some_packed_struct' may result in an unaligned pointer value [-Werror=address-of-packed-member]
Most of the time, those warnings are generated when passing a pointer to a member of a structure to a function. For example:
int bar(const int *val)
{ ... }
struct {
int a;
char b;
int c;
} __attribute__((packed)) foo;
// &foo.c is not aligned
bar(&foo.c)
There are a lot of different solutions to this problem. One that immediately comes to mind is to memcpy()
the value to a stack-allocated variable of the same type and pass a pointer to this stack variable.
int tmp;
memcpy(&tmp, &foo.c, sizeof(foo.c));
bar(&tmp)
While this works, it will result in a lot of boiler-plate code which I would rather avoid introducing.
For now, I have been contemplating using a macro of the form
#define ALIGN_VALUE_PTR(val) (&((const typeof(val)) { val }))
bar(ALIGN_VALUE_PTR(foo.c));
This works well for scalar types. However, and predictably, if val
is a struct
, this will not work.
struct inner {
int c, d;
};
struct outer {
int a, b;
struct inner inner;
};
void print_inner_field(const struct inner *inner)
{
printf("value is %d\n", inner->c);
}
struct outer outer;
print_inner_field(ALIGN_VALUE_PTR(outer.inner));
lol.c:28:30: error: incompatible types when initializing type ‘int’ using type ‘struct inner’
28 | print_inner(ALIGN_VALUE_PTR(foo.inner));
| ^~~
lol.c:3:55: note: in definition of macro ‘ALIGN_VALUE_PTR’
3 | #define ALIGN_VALUE_PTR(val) (&((const typeof(val)) { val }))
| ^~~
I'm wondering if anyone has a better idea than what I have come up with.
Upvotes: 3
Views: 687
Reputation: 215567
#define ALIGN_VALUE_PTR(val) (((const typeof(val) []) { val }))
The reason your original version does not work is because of subtleties how braces are treated in initializers, and the fact that compound literals always require at least one level of braces. You can initialize a struct as
struct s bar;
//...
struct s foo = bar;
but you can't do:
struct s foo = { bar };
because, inside the braces, bar
would have to have type matching the first member of struct s
, not struct s
.
Using an array (or a structure; there are variants involving a gratuitous structure too) allows you to match the brace levels to use the struct type you wanted as the initializer. The array form of course starts out with array type and only becomes a pointer via decay. If you want to ensure it's always a pointer, add a gratuitous +0
or &*
to force the decay.
Upvotes: 5