문제

I am unsure of whether or not the code has pointer aliasing (or other standard conformance issues) in the asserts cast. It seems that a pointer to the union type should be able to be cast to a pointer of the first member and since the union is only composed of these two structs, I think a cast to the first member should work, but I'm not sure if this is correct or if I'm glossing over padding details in the process. Are unions required to pad the upper bits?

It seems as this is unspecified behavior? Does anyone have any insight as to whether this is suported. I know that there is an alternative standard way of doing this by using a struct with a enum type field and struct container_storage member, but it seems like a waste of space considering that this information is already in struct contained

compilation command in linux: gcc -std=c99 -Wextra -pedantic -fstrict-aliasing test.c && ./a.out && echo $? returns 0

#include <stdlib.h>
#include <assert.h>

enum type {type_a = 1, type_b = 2};

struct contained {
    int some_other_field;
    enum type type;
};

struct container_a {
    struct contained contained;
    int test;
};


struct container_b {
    struct contained contained;
    char test;
};

union container_storage {
    struct container_a container_a;
    struct container_b container_b;
};

int
main(int argc, char **argv)
{
    union container_storage a =
        {.container_a = {.contained = {.type = type_a}, .test = 42}};
    union container_storage b =
        {.container_b = {.contained = {.type = type_b}, .test = 'b'}};

    assert(((struct contained *)&a)->type == type_a);
    assert(((struct contained *)&b)->type == type_b);

    return EXIT_SUCCESS;
}

References:

[1] gcc, strict-aliasing, and casting through a union

[2] What is the strict aliasing rule?

도움이 되었습니까?

해결책

That should be fine. C11, 6.5.2.3/6 ("Structure and union members") says:

One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the completed type of the union is visible. Two structures share a common initial sequence if corresponding members have compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.

(C++ makes the same guarantee (C++11, 9.2/18) for standard-layout unions.)

다른 팁

union don't pad, they just overlay their members. The first member of any struct is guaranteed to start right off, without padding. In general struct that start with the same members of same type are guaranteed to have the same layout for that initial part.

Under C89, a pointer of structure type which identifies a member of a union may be used to inspect any member which is part of a Common Initial Sequence shared with the type of data stored therein. This in turn generally implies that a pointer to any structure type could be used to inspect any member of the Common Initial Sequence shared with any other type (such behavior would have been unambiguously defined if the object happened to be a member of a declared union object, and the only practical way for a compiler to yield the required behavior in those cases would be to uphold it for all).

C99 added an additional requirement that the CIS guarantees only apply when a complete union type containing both structures is visible, which some compiler writers seem to think means it only applies to accesses performed directly through union types. The authors of such compilers seem to think a function that would need to handle functions with a common header like:

struct smallThing { void *next; uint16_t length; uint8_t dat[2]; };
struct bigThing { void *next; uint16_t length; uint8_t dat[65528]; };

should be to extract out the header like:

struct uHeader {  void *next; uint16_t length; };
struct smallThing { uHeader head; uint8_t dat[2]; };
struct bigThing { uHeader head; uint8_t dat[15994]; };

or use union-type objects for everything, even though using uHeader would increase the size of struct smallThing by 50% (and totally break any code that had been reliant upon its layout), and using unions for everything when most objects only need to be small would increase memory usage a thousandfold.

If one needs code to be compatible with compilers that essentially ignore the Common Initial Sequence rule, one should regard the Common Initial Sequence rule as essentially useless. Personally, I think it would be better to document that only compilers that honor the CIS should be considered suitable for use with one's code, rather than bending over backward to accommodate unsuitable compilers, but I think it's important to be aware that compilers like the latter ones exist.

So far as I can tell, clang and gcc do not honor the CIS rule in any useful way except when the -fno-strict-aliasing flag is set. I don't know about other compilers.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top