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.
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:
bug report
patch
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.
Related
struct details_state {
struct details_status D1;
struct details_status D2;
};
struct details {
struct details_state details_states[2];
} __attribute__((packed));
struct details *p;
void print_details_status(struct details_status *s)
print_details_status(&(p->details_states[i].D1));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: taking address of packed member of 'struct details' may result in an unaligned pointer value [-Waddress-of-packed-member]
GCC gives this warning with >9 version. How to get rid of this warning without using [-Wno-address-of-packed-member]
In the comments to your post you have the explanation of the why it warns and why it can be a bad idea to take a pointer of a non-aligned member. The issue is not so much about assigning an unaligned value to a pointer (which seems to be permitted by bullet 5 of 6.3.2.3 here, but only about dereferencing it. Dereferencing it seems to be even explicitly permitted on some architectures, albeit with a performance penalty (e.g.: on x86 when not using SSE load/store), but it may cause severe issues depending on type and generated assembly on other architectures, typically a BUS ERROR if you are in user space or a fault if you are in kernel space.
For bad that it may be to do something like that, there is code around that takes pointers of packed struct's elements
and even dereferences them without shame:
https://github.com/abperiasamy/rtl8812AU_8821AU_linux/blob/4d1726146fd96552c9fa5af05c75187027d6885b/core/rtw_cmd.c#L1481
https://github.com/abperiasamy/rtl8812AU_8821AU_linux/blob/4d1726146fd96552c9fa5af05c75187027d6885b/core/rtw_mlme.c#L3689
I can imagine that someone may be in a situation where they are stuck with someone else's code with a packed struct (note that it would be bad to have a packed struct in a library's header file, as packing is a non-standard extension to the language and its results are compiler dependent). Or maybe they have a function that is guaranteed to deal properly with an unaligned pointer; as the C standard provides no way to communicate that in the code, one would have to rely on the function's documentation (and/or implementation) to figure that.
Now that you understand what's wrong with it but also that it can sometimes be useful to suppress that warning locally without setting a global compiler option; here's an answer to:
How to get rid of this warning without using [-Wno-address-of-packed-member]
You can use the base address to the struct and add to it the relevant offset, obtained via offsetof(). This requires some casting and, even turned into a macro, it is not particularly clean as it requires several arguments:
#define member_nowarn(structType, elementType, structPtr, memberName) \
((elementType*)(((char*)(structPtr)) + offsetof(structType, memberName)))
This can be simplified so you don't have to pass the elementType, but then the return value of the macro will be a void* which means compiler warnings may get suppressed when you mistakenly pass it to a function that expects a pointer to something else than the type of the memberName element, so I wouldn't recommend this:
#define member_nowarn_unsafe(structType, structPtr, memberName) \
((void*)((((char*)(structPtr)) + offsetof(structType, memberName))))
If your compiler supports the non-standard typeof() (e.g.: this is provided by stddef.h in gcc), you can simplify the macro so that no type has to be passed, the returning pointer's type is still correct and the code using it becomes simpler and less error-prone:
#define member_nowarn_nostd(structPtr, memberName) \
((typeof((structPtr)->memberName)*)(((char*)(structPtr)) + offsetof(typeof(*(structPtr)), memberName)))
Example code:
#include <stdio.h>
#include <stddef.h>
#include <string.h>
typedef float MYTYPE;
struct details {
char mychar;
MYTYPE mymember;
} __attribute__((packed));
struct details d;
void myfunc(MYTYPE*);
#define member_nowarn(structType, elementType, structPtr, memberName) \
((elementType*)(((char*)(structPtr)) + offsetof(structType, memberName)))
#define member_nowarn_unsafe(structType, structPtr, memberName) \
((void*)((((char*)(structPtr)) + offsetof(structType, memberName))))
#define member_nowarn_nostd(structPtr, memberName) \
((typeof((structPtr)->memberName)*)(((char*)(structPtr)) + offsetof(typeof(*(structPtr)), memberName)))
int main()
{
d.mymember = 123;
// warns
myfunc(&d.mymember);
//warns
myfunc((MYTYPE*)&d.mymember);
// ugly, but it does't warn
myfunc(((MYTYPE*)((char*)(&d) + offsetof(struct details, mymember))));
// same, turned into a macro
myfunc(member_nowarn(struct details, MYTYPE, &d, mymember));
// simpler, but the return value of the macro is void* so you won't get a
// warning when passing to a function that requires a different type of pointer
myfunc(member_nowarn_unsafe(struct details, &d, mymember));
// simpler macro, but uses non-standard typeof()
myfunc(member_nowarn_nostd(&d, mymember));
return 0;
}
void myfunc(MYTYPE* arg)
{
MYTYPE val;
// do not dereference the pointer: it may crash
// depending on MYTYPE, architecture and the generated
// assembly.
// val = *arg; // don't do this
// instead use memcpy to get the data it points to
memcpy(&val, arg, sizeof(val));
printf("%p %f\n", arg, (double)val);
}
I've been writing embedded C code for many years now, and the newer generations of compilers and optimizations have certainly gotten a lot better with respect to their ability to warn about questionable code.
However, there is at least one (very common, in my experience) use-case that continues to cause grief, wheres a common base type is shared between multiple structs. Consider this contrived example:
#include <stdio.h>
struct Base
{
unsigned short t; /* identifies the actual structure type */
};
struct Derived1
{
struct Base b; /* identified by t=1 */
int i;
};
struct Derived2
{
struct Base b; /* identified by t=2 */
double d;
};
struct Derived1 s1 = { .b = { .t = 1 }, .i = 42 };
struct Derived2 s2 = { .b = { .t = 2 }, .d = 42.0 };
void print_val(struct Base *bp)
{
switch(bp->t)
{
case 1:
{
struct Derived1 *dp = (struct Derived1 *)bp;
printf("Derived1 value=%d\n", dp->i);
break;
}
case 2:
{
struct Derived2 *dp = (struct Derived2 *)bp;
printf("Derived2 value=%.1lf\n", dp->d);
break;
}
}
}
int main(int argc, char *argv[])
{
struct Base *bp1, *bp2;
bp1 = (struct Base*) &s1;
bp2 = (struct Base*) &s2;
print_val(bp1);
print_val(bp2);
return 0;
}
Per ISO/IEC9899, the casts within code above should be OK, as it relies on the first member of the structure sharing the same address as the containing structure. Clause 6.7.2.1-13 says so:
Within a structure object, the non-bit-field members and the units in which bit-fields
reside have addresses that increase in the order in which they are declared. A pointer to a
structure object, suitably converted, points to its initial member (or if that member is a
bit-field, then to the unit in which it resides), and vice versa. There may be unnamed
padding within a structure object, but not at its beginning.
The casts from derived to base work fine, but the cast back to the derived type within print_val() generates an alignment warning. However this is known to be safe as it is specifically the "vice versa" part of the clause above. The problem is that the compiler simply doesn't know that the we've already guaranteed that the structure is in fact an instance of the other type via other means.
When compiled with gcc version 9.3.0 (Ubuntu 20.04) using flags -std=c99 -pedantic -fstrict-aliasing -Wstrict-aliasing -Wcast-align=strict -O3 I get:
alignment-1.c: In function ‘print_val’:
alignment-1.c:30:31: warning: cast increases required alignment of target type [-Wcast-align]
30 | struct Derived1 *dp = (struct Derived1 *)bp;
| ^
alignment-1.c:36:31: warning: cast increases required alignment of target type [-Wcast-align]
36 | struct Derived2 *dp = (struct Derived2 *)bp;
| ^
A similar warning occurs in clang 10.
Rework 1: pointer to pointer
A method used in some circumstances to avoid the alignment warning (when the pointer is known to be aligned, as is the case here) is to use an intermediate pointer-to-pointer. For instance:
struct Derived1 *dp = *((struct Derived1 **)&bp);
However this just trades the alignment warning for a strict aliasing warning, at least on gcc:
alignment-1a.c: In function ‘print_val’:
alignment-1a.c:30:33: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
30 | struct Derived1 *dp = *((struct Derived1 **)&bp);
| ~^~~~~~~~~~~~~~~~~~~~~~~~
Same is true if cast done as an lvalue, that is: *((struct Base **)&dp) = bp; also warns in gcc.
Notably, only gcc complains about this one - clang 10 seems to accept this either way without warning, but I'm not sure if that's intentional or not.
Rework 2: union of structures
Another way to rework this code is using a union. So the print_val() function can be rewritten something like:
void print_val(struct Base *bp)
{
union Ptr
{
struct Base b;
struct Derived1 d1;
struct Derived2 d2;
} *u;
u = (union Ptr *)bp;
...
The various structures can be accessed using the union. While this works fine, the cast to a union is still flagged as violating alignment rules, just like the original example.
alignment-2.c:33:9: warning: cast from 'struct Base *' to 'union Ptr *' increases required alignment from 2 to 8 [-Wcast-align]
u = (union Ptr *)bp;
^~~~~~~~~~~~~~~
1 warning generated.
Rework 3: union of pointers
Rewriting the function as follows compiles cleanly in both gcc and clang:
void print_val(struct Base *bp)
{
union Ptr
{
struct Base *bp;
struct Derived1 *d1p;
struct Derived2 *d2p;
} u;
u.bp = bp;
switch(u.bp->t)
{
case 1:
{
printf("Derived1 value=%d\n", u.d1p->i);
break;
}
case 2:
{
printf("Derived2 value=%.1lf\n", u.d2p->d);
break;
}
}
}
There seems to be conflicting information out there as to whether this is truly valid. In particular, an older aliasing write-up at https://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html specifically calls out a similar construct as being invalid (see Casting through a union (3) in that link).
In my understanding, because pointer members of the union all share a common base type, this doesn't actually violate any aliasing rules, because all accesses to struct Base will in fact be done via an object of type struct Base - whether by dereferencing the bp union member or by accessing the b member object of the d1p or d2p. Either way it is accessing the member correctly via an object of type struct Base - so as far as I can tell, there is no alias.
Specific Questions:
Is the union-of-pointers suggested in rework 3 a portable, safe, standards compliant, acceptable method of doing this?
If not, is there a method that is fully portable and standards compliant, and does not rely on any platform-defined/compiler-specific behavior or options?
It seems to me that since this pattern is fairly common in C code (in the absence of true OO constructs like in C++) that it should be more straightforward to do this in a portable way without getting warnings in one form or another.
Thanks in advance!
Update:
Using an intermediate void* may be the "right" way to do this:
struct Derived1 *dp = (void*)bp;
This certainly works but it really allows any conversion at all, regardless of type compatibility (I suppose the weaker type system of C is fundamentally to blame for this, what I really want is an approximation of C++ and the static_cast<> operator)
However, my fundamental question (misunderstanding?) about strict aliasing rules remains:
Why does using a union type and/or pointer-to-pointer violate strict aliasing rules? In other words what is fundamentally different between what is done in main (taking address of b member) and what is done in print_val() other than the direction of the conversion? Both yield the same situation - two pointers that point to the same memory, which are different struct types - a struct Base* and a struct Derived1*.
It would seem to me that if this were violating strict aliasing rules in any way, the introduction of an intermediate void* cast would not change the fundamental problem.
You can avoid the compiler warning by casting to void * first:
struct Derived1 *dp = (struct Derived1 *) (void *) bp;
(After the cast to void *, the conversion to struct Derived1 * is automatic in the above declaration, so you could remove the cast.)
The methods of using a pointer-to-a-pointer or a union to reinterpret a pointer are not correct; they violate the aliasing rule, as a struct Derived1 * and a struct Base * are not suitable types for aliasing each other. Do not use those methods.
(Due to C 2018 6.2.6.1 28, which says “… All pointers to structure types shall have the same representation and alignment requirements as each other…,” an argument can be made that reinterpreting one pointer-to-a-structure as another through a union is supported by the C standard. Footnote 49 says “The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.” At best, however, this is a kludge in the C standard and should be avoided when possible.)
Why does using a union type and/or pointer-to-pointer violate strict aliasing rules? In other words what is fundamentally different between what is done in main (taking address of b member) and what is done in print_val() other than the direction of the conversion? Both yield the same situation - two pointers that point to the same memory, which are different struct types - a struct Base* and a struct Derived1*.
It would seem to me that if this were violating strict aliasing rules in any way, the introduction of an intermediate void* cast would not change the fundamental problem.
The strict aliasing violation occurs in aliasing the pointer, not in aliasing the structure.
If you have a struct Derived1 *dp or a struct Base *bp and you use it to access a place in memory where there actually is a struct Derived1 or, respectively, a struct Base, then there is no aliasing violation because you are accessing an object through an lvalue of its type, which is allowed by the aliasing rule.
However, this question suggested aliasing a pointer. In *((struct Derived1 **)&bp);, &bp is the location where there is a struct Base *. This address of a struct Base * is converted to the address of a struct Derived1 **, and then * forms an lvalue of type struct Derived1 *. The expression is then used to access a struct Base * using a type of struct Derived1 *. There is no match for that in the aliasing rule; none of the types it lists for accessing a struct Base * are a struct Derived1 *.
Linux kernel offers an interesting alternative to described concepts.
It's based on idea of embedding.
struct Base {
int x;
};
struct Derived {
struct Base base; // base is embedded into Derived
int y;
};
Transformation from a pointer to derived is easy:
struct Derived derived;
struct Base* base = &derived.base;
The reverse transformation is done using container_of macro.
This macro is bit tricky but it can be simplified subtracting offset of base member within Derived from the pointer to Base.
(struct Derived *)(void*)((char*)base - offsetof(struct Derived, base))
Casting to char* is essential because:
allow pointer arithmetic with result of offsetof()
as char* can alias with everything, the code is correct with strict aliasing rules
casting through void* is used to silence -Wcast-align=strict as resulting object will be correctly aligned
Examplary usage:
struct Derived* derived = container_of(base, struct Derived, base);
This approach is attractive because:
the pointer operation is a simple subtraction, no need do dereference any pointer, no extra cache miss
the base class can be located anywhere within the derived structure
easy support for multiple inheritance
structure layout can be modified without breaking existing code
With a help of function pointers, the polymorphism can be robustly implemented.
#include <stdio.h>
#include <stddef.h>
#define container_of(ptr, type, member) \
(type*)(void*)((char*)ptr - offsetof(type, member))
struct Person {
int age;
void (*greet)(struct Person *);
};
struct NamedPerson {
char name[32];
struct Person base;
};
void NamedPerson_greet(struct Person *p) {
struct NamedPerson *np = container_of(p, struct NamedPerson, base);
printf("Hello!. My name is %s, and I'm %d years old.\n",
np->name, np->base.age);
}
struct NamedPerson George = {
.name = "George",
.base.age = 42,
.base.greet = NamedPerson_greet,
};
int main() {
struct Person *person = &George.base;
person->greet(person); // Hello, my name is George ...
}
Compiles without any warnings with all aliasing warnings enabled, and in pedantic mode.
gcc prog.c -std=c99 -Wall -pedantic -fstrict-aliasing -Wstrict-aliasing -Wcast-align=strict -O3
To be clear, the original code is correct and does not require re-working ; the only issue is an unaesthetic warning .
The rest of the question, and the answers so far, focus on how to mangle the code in order to coax the compiler to not issue a warning .
IMHO it's preferable to deal directly with undesirable warnings, instead of mangling the code. Because mangled code is harder to read and understand; and future versions of the compiler might change the way that warnings are triggered.
Approaches along this line would include:
disabling that warning entirely
conditionally disabling that warning for sections of code that perform the safe operation (perhaps with assistance of a macro or inline function)
filtering the compiler output (e.g. via grep -v) to remove the warning
I am working on fixing the below problem in C-program on Linux (4.19.21-linux-gnu-gcc),
//Structure definition :
struct data_from_u64 {
uint32_t u32_value;
uint16_t u16_value;
uint8_t u8_value1;
uint8_t u8_value2;
}
//Expectation : Convert a incoming uint64_t variable to the above structure using the below macro,
#define U64_TO_STRUCT(u64_value) ( * (data_from_u64 *) ((void*) &(u64_value)) )
//Usage
data_from_u64 data = U64_TO_STRUCT(u64_value);
The project is huge that it is very widely used, but the new compilation procedure is enforcing "-O2" and "-Werror" flag while compiling. Can you help resolve the error/warning mentioned below. change should be minimal as this macro is used in more than 1000 places.
Error/Warning:
error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
#define U64_TO_STRUCT(u64_value) ( * (data_from_u64 ) ((void) &(u64_value)) )
What have i tried :
I have tried doing memcpy, it works. But it requires to be updated in all the 1000places of usage[it requires parameter of the macro to be changed to include the destination].
An alternative to memcpy or the improper aliasing is to use a union with a compound literal:
#define U64_TO_STRUCT(u64_value) \
((union { uint64_t u; struct data_from_u64 s; }) { u64_value } .s)
You can use this:
struct data_from_u64 U64_TO_STRUCT(uin64_t u64_value) {
struct data_from_u64 ret;
memcpy(&ret, &u64_value, sizeof u64_value);
return ret;
}
It's a common misconception that you cannot return structs like this. You cannot return local arrays, but returning structs is perfectly fine.
And the lesson to learn from this, is that all nagging from us here at SO about "you're violating the anti aliasing rule" is not just some nerdy mumbojumbo that never matters in practice. As you can see for yourself, it does have real world consequences. :)
If you really want to have a macro for this, take a look at Eric's answer which works great, conforms to the standard and does not rely on compiler extensions. I personally just don't like function like macros.
In the following code...
#include <stdlib.h>
#include <stdint.h>
extern void get_buffer_from_HW_driver(volatile uint32_t **p);
void getBuffer(volatile uint32_t **pp)
{
// Write an address into pp, that is obtained from a driver
// The underlying HW will be DMA-ing into this address,
// so the data pointed-to by the pointer returned by this
// call are volatile.
get_buffer_from_HW_driver(pp);
}
void work()
{
uint32_t *p = NULL;
getBuffer((volatile uint32_t **)&p);
}
...the compiler rightfully detects that any potential accesses to the data pointed to by p inside work are dangerous accesses. As-is, the code instructs the compiler that it is safe to emit code that optimizes away repeated read accesses to *p - which is indeed wrong.
But the weird thing is, that the warning emitted by compiling this code...
$ gcc -c -Wall -Wextra -Wcast-qual constqual.c
...doesn't complain about the loss of volatile - it instead recommends using const:
constqual.c: In function ‘work’:
constqual.c:20:15: warning: to be safe all intermediate pointers in cast from
‘uint32_t ** {aka unsigned int **}’ to ‘volatile uint32_t **
{aka volatile unsigned int **}’ must be ‘const’ qualified
[-Wcast-qual]
getBuffer((volatile uint32_t **)&p);
^
I cannot see how const makes sense here.
P.S. Note that adding volatile in front of the uint32_t *p, as expected, fixes the issue. My question is why GCC recommends const instead of volatile.
Well, I raised a ticket in GCC's Bugzilla about this... and Joseph Myers has answered with a laconic answer:
No, GCC is not confused. It's saying that it's type-safe to convert
uint32_t ** to volatile uint32_t *const *, but not to convert it to
volatile uint32_t *.
...and he also added a reference to this part of the C FAQ.
I have to admit that my first reaction to this was a "say what?". I quickly tested the suggestion, changing the code to make it use the proposed declaration (and cast) instead...
#include <stdlib.h>
#include <stdint.h>
extern void get_buffer_from_HW_driver(volatile uint32_t * const *p);
void getBuffer(volatile uint32_t * const *pp)
{
// Write an address into pp, that is obtained from a driver
// The underlying HW will be DMA-ing into this address,
// so the data pointed-to by the pointer returned by this
// call are volatile.
get_buffer_from_HW_driver(pp);
}
void work()
{
uint32_t *p = NULL;
getBuffer((volatile uint32_t * const *)&p);
}
$ gcc -c -Wall -Wextra -Wcast-qual constqual.c
$
...and indeed, no warning anymore.
So I went ahead and read the relevant FAQ - and I think I understand a bit more of what is happening. By adding the const modifier, the parameter we are passing is (reading from right to left, as we're supposed to do in this kind of C syntax):
a pointer to a constant pointer (that will never change) that points to volatile data
This indeed maps very well to what is happening here: I am getting a pointer that points to volatile data, that is a driver-provided buffer - i.e. one that I indeed am not allowed to change, since it comes from pre-allocated lists of buffers that the driver itself allocated. Modifying the pointer that get_buffer_from_HW_driver returned would make no sense; it's not mine to modify, I can only use it as-is.
I confess I am really surprised that C's typesystem (augmented with the really strong static-analysis checks of -Wcast-qual) can actually help in guaranteeing these semantics.
Many thanks to Joseph - and I'll leave the question open for a few weeks, in case someone else wants to elaborate more.
P.S. Adding a mental note: from now on, when anyone claims that C is a simple language, I think I'll point them here.
I've used this interesting approach to make opaque and private structs:
struct s_vector_private
{
size_t item_size;
uint32_t used_slots;
uint32_t buffer_total_slots;
uint8_t * buffer;
};
typedef uint8_t vector_t[sizeof(struct s_vector_private)];
/* This code can be found at [1] */
This way, vector_t contents are private and vector_t can be allocated on heap and stack.
Recently I've wondered if allocating vector_t, either in stack or heap, can cause alignment problems since compiler sees it is an array instead of a struct.
For further enlightenment, there are big libraries that use this kind of approach, like zmq declaration of the zmq_msg_t type:
typedef struct zmq_msg_t {unsigned char _ [40];} zmq_msg_t;
/* This code can be found at [2] */
In the zmq example, is it possible to get misaligned structures as well? I couldn't avoid noting that zmq_msg_t has declared the array inside of a struct, would this be a safe way to implement the technique in question here?
[1] http://github.com/felipe-lavratti/chelper/blob/master/include/chelper/helper_types.h, line 48.
[2] http://github.com/zeromq/libzmq/blob/master/include/zmq.h, line 202.
It is necessary to use either C11 or compiler extensions to align vector_t. For example, for a truly opaque type with flexible size and alignment, that means a declarator like this:
#define stack_alloc_opaque(identifier) \
alignas(alignof(max_align_t)) char (identifier)[sizeof_opaque_type()]
Where sizeof_opaque_type() is a function returning the true size at runtime.
If you don't need the full flexibility of an opaque type you must at least align by alignof(struct s_vector_private), but this limits your ability to alter the type behind the scenes.
While you could probably get away with the alignment mismatch on x86, it is undefined behavior to cast between pointers with different alignment requirements, and accessing such an object is definitely not portable.
Speaking of which, there's an elephant in the room here.
Doing the above will make the types perfectly compatible at the byte level, but it's skirting strict aliasing rules. The exception that that allows char* to alias any other type is one-way only -- vector_t being declared as uint8_t[] (or even char[]) makes that its effective type, so it can't be punned to something else. That leaves you with a few options:
Use a compiler extension like __attribute__((may_alias)) on s_vector_private
Disable strict aliasing optimizations altogether (-fno-strict-aliasing)
Leave them on, but test carefully with awareness that problems may occur in the future or in other configurations
Don't use opaque types on the stack
Unfortunately the only really portable and safe one is the last. I like this approach too, but I don't know of any other way around that particular issue.
The technique won't cause problems with heap allocations. The pointer returned by malloc() et al is aligned sufficiently for any use — the malloc() function cannot tell whether it is allocating a 40-byte string that could be on any byte boundary or an array of a type that needs to be aligned on an 8-byte boundary, so it takes the conservative view that it is always allocating for the most stringent alignment that the implementation requires.
The technique could in theory cause problems with stack allocation. If you have:
char c1;
vector_t v1;
char c2;
then you could have v1 misaligned. It probably won't be a problem, but if you wish to be safe, you avoid the possibility. However, GCC 4.8.2 running on Mac OS X 10.9.1 seems to go to some lengths to avoid trouble:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror alx.c -o alx
$ alx
24
&c1 = 0x7fff54b9552d
&v1 = 0x7fff54b95530
&c2 = 0x7fff54b9552e
&p1 = 0x7fff54b95550
&c3 = 0x7fff54b9552f
$ gcc -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror alx.c -o alx
$ alx
24
&c1 = 0x7fff579a756f
&v1 = 0x7fff579a7550
&c2 = 0x7fff579a754f
&p1 = 0x7fff579a7530
&c3 = 0x7fff579a752f
$
#include <stdint.h>
#include <stdio.h>
struct s_vector_private
{
size_t item_size;
uint32_t used_slots;
uint32_t buffer_total_slots;
uint8_t *buffer;
};
typedef uint8_t vector_t[sizeof(struct s_vector_private)];
int main(void)
{
char c1;
vector_t v1;
char c2;
struct s_vector_private p1;
char c3;
printf("%zu\n", sizeof(vector_t));
printf("&c1 = %p\n", (void *)&c1);
printf("&v1 = %p\n", (void *)&v1);
printf("&c2 = %p\n", (void *)&c2);
printf("&p1 = %p\n", (void *)&p1);
printf("&c3 = %p\n", (void *)&c3);
return 0;
}
Also, the technique is a trifle odd. It hasn't really hidden the underlying type (the code can still see it); it just isn't called vector_t.
Posix c library uses the zmq mentioned technique to hide struct contents. To deal with alignment they do a union with the array. See ptread_mutexattr_t type below.
typedef union
{
char __size[__SIZEOF_PTHREAD_MUTEXATTR_T];
int __align;
} pthread_mutexattr_t;
This is an excellent approach to hide struct members, but there is one main drawback that may be significant if your code is not a library with slow releases. the size of the array has got to be a typed constant:
#ifdef __x86_64__
# if __WORDSIZE == 64
# define __SIZEOF_PTHREAD_ATTR_T 56
# define __SIZEOF_PTHREAD_MUTEX_T 40
# define __SIZEOF_PTHREAD_MUTEXATTR_T 4
...
This is a considerable drawback in rapid changing modules, or code under development, since each time the struct internals are changed, this number must be manually update. Forgetting to do it will lead to undefined behaviour hard to track down.
There is not a problem here. To understand why, you need to see how sizeof works with structs that do not have __attribute__ ((__packed__)) or explicit alignment.
Try the following program:
#include <stdio.h>
#include <stdlib.h>
struct st
{
void *p;
char c;
};
struct st arr[5];
int
main (int argc, char **argv)
{
printf ("Size of struct: %lu\nSize of array: %lu\n",
sizeof (struct st),
sizeof (arr));
exit (0);
}
The output is as follows (on x86_64):
Size of struct: 16
Size of array: 80
This marginally surprising result is because while struct st appears to need only 9 bytes (8 for the pointer, and one for the char), it actually takes 16 for precisely the reason that an array of them must have proper alignment. sizeof thus includes the padding bytes. Hence 5 of them take 80 bytes, and each of the pointers is correctly aligned.
Even within the structure, without __attribute__ ((__packed__)), padding bytes will be inserted to align the types properly (preventing this is precisely what __attribute__ ((__packed__)) is for).
Re the ZMQ example, I am not familiar with the code, but I am guessing they are defining the wire format for a message, which is of constant byte size. At some point they will cast part or all of it to a structure, probably with __attribute__ ((__packed__)), being careful about what falls where. Why do this rather than just define a structure for the entire message? This may be so that if someone changes the struct, they get an immediate failure as opposed to a working but incompatible protocol. But that bit is just speculation.