Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
521 views
in Technique[技术] by (71.8m points)

c - union of structs sharing same first members

I have been looking into an un-traditional way of achieving struct "polymorphism" in pre-C11 C. Let's say we have 2 structs:

struct s1 {
    int var1;
    char var2;
    long var3;
};

struct s2 {
    int var1;
    char var2;
    long var3;
    char var4;
    int var5;
};

On most compilers, we could safely cast between pointers to the two and then access the common first members if no padding takes place. However, this is not standartized behaviour.

Now, I found the following line in the C standard as far as C89:

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, and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them. Two structures share a common initial sequence if corresponding members have compatible types for a sequence of one or more initial members.

It also states the following:

A pointer to a union object, suitably cast, points to each of its members (or if a member is a bit-field, then to the unit in which it resides), and vice versa.

Now, if I create a union of these two structs:

union s2_polymorphic {
    struct s1 base;
    struct s2 derived;
};

And use it this way:

union s2_polymorphic test_s2_polymorphic, *ptest_s2_polymorphic;
struct s2 *ptest_s2;
struct s1 *ptest_s1;

ptest_s2_polymorphic = &test_s2_polymorphic;

ptest_s2 = (struct s2*)ptest_s2_polymorphic;

ptest_s2->var1 = 1;
ptest_s2->var2 = '2';

ptest_s1 = (struct s1*)ptest_s2;

printf("ptest_s1->var1 = %d
", ptest_s1->var1);
printf("ptest_s1->var2 = %c
", ptest_s1->var2);

Which compiles and runs fine and gives, on gcc (GCC) 4.8.3 20140911, the output

ptest_s1->var1 = 1                                                            
ptest_s1->var2 = 2

Will the behaviour be well-defined, according to the quotes from the standard given above?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

After some research, I think I have a qualified answer for this question.

The citation given was from the C89 standard. C99 and C11 have it rephrased like this:

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.

The last part can be interpreted, IMHO, in a variety of ways. However, the commitee left it as it is. According to them, it means that the inspection of the "common initial part" of the structures can be done only using an object of the union type that has been declared to contain them. That is very well demonstrated in this question.

What about C89, which seems to allow what I was trying to do? Well, in a C89-conforming compiler, yes, this should work. However, it might not really be needed: I don't know of a single strictly-C89-conforming compiler that supports strict aliasing, so, with them, it is easier to simply cast the structs with the common initial sequence to each other's types and try not to give them different packing settings. The result should be the same.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...