3 ms·
I would be surprised if C guarantees those x and y to have same offsets Plus you cant cast and dereference one type's pointer to the other. That would be UB
by shultays 4y ago
I would be surprised if C guarantees those x and y to have same offsets
Plus you cant cast and dereference one type's pointer to the other. That would be UB
- spacedcowboy 4y agoModulo any padding rules, I would be surprised if C didn't store them in the same offsets, the compatible-type specification[1] and equality operator[2] (also see footnote 109) would make implementation much harder if you tried to do it any other way. The way to work around that of course, and to make sure that no matter what, the parent/child objects are compatible to the parent-only level is to do something like: // this structure defines a node of a linked list and // it only holds the pointers to the next and the previous // nodes in the linked list. struct list_head { struct list_head *next; // pointer to the node next to the current one struct list_head *prev; // pointer to the node previous to the current one }; // list_int holds a list_head and an integer data member struct list_int { struct list_head list; // common next and prev pointers int value; // specific member as per implementation }; // list_str holds a list_head and a char * data member struct list_str { struct list_head list; // common next and prev pointers char \* str; // specific member as per implementation }; Often the 'parent' structure would have an 'int objType', which can be switch'd on, to make sure that the receiving function knows what to do. I'm not really seeing any undefined behaviour here. I'm pretty sure this technique is decades old, btw. I know at one point the linux kernel used it, not sure if it still does.. If you're happy with being C11-compliant, then remove any names for the 'parent' structure in the child structures, making them "anonymous structs" at which point they are [3] considered to be part of the same struct as the parent. [1]: http://port70.net/~nsz/c/c11/n1570.html#6.2.7p1 http://port70.net/~nsz/c/c11/n1570.html#6.2.7p1 [2]: http://port70.net/~nsz/c/c11/n1570.html#6.5.9p6 http://port70.net/~nsz/c/c11/n1570.html#6.5.9p6 [3]: http://port70.net/~nsz/c/c11/n1570.html#6.7.2.1p13 http://port70.net/~nsz/c/c11/n1570.html#6.7.2.1p13
- hddqsb 4y agoEven if the fields are stored in the same offsets, there are modern optimisations that rely on "strict aliasing" and that would cause issues (unless the workaround you described is used).
- pjdesno 4y agoIt may be strictly UB, but given that any operating system written in C relies on this behavior being well-defined (e.g. the container_of macro widely used in the Linux kernel) you're probably pretty safe. Note that there is sort of an active war between the OS folks, who are probably the main users of pure C nowadays, and the UB nazis among the compiler folks who are mostly worried about efficiently optimizing complex template code in C++ and don't care whether their computers continue to run :-)
- leni536 4y agoThe linux kernel compiles with no strict aliasing, so kernel code is unaffected. In userspace this is still dodgy, strict aliasing does affect generated code, and correctness of code that relies on UB this way. A pragmatic solution would be attributes that allowed declaring that certain pairs of types that are allowed to alias with each other. It would even be better if the C and C++ standards provided facilities for this, although it can be challenging to rigurously fit into the object model.