How to Compile C Code With Anonymous Structs/Unions

How to compile C code with anonymous structs / unions?

according to http://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html#Unnamed-Fields

-fms-extensions will enable the feature you (and I) want.

When are anonymous structs and unions useful in C11?

Anonymous union inside structures are very useful in practice. Consider that you want to implement a discriminated sum type (or tagged union), an aggregate with a boolean and either a float or a char* (i.e. a string), depending upon the boolean flag. With C11 you should be able to code

typedef struct {
bool is_float;
union {
float f;
char* s;
};
} mychoice_t;

double as_float(mychoice_t* ch)
{
if (ch->is_float) return ch->f;
else return atof(ch->s);
}

With C99, you'll have to name the union, and code ch->u.f and ch->u.s which is less readable and more verbose.

Another way to implement some tagged union type is to use casts. The Ocaml runtime gives a lot of examples.

The SBCL implementation of Common Lisp does use some union to implement tagged union types. And GNU make also uses them.

Reaching named struct in Unnamed union in C

Per this answer, the solution for gcc is to compile with -fms-extensions.

Here is an example. Tested with Visual C++ and MSYS2 gcc 10.2.
With this version of gcc, the -fms-extensions seems to be implied, and I get an error as mentionned in the comments if I compile instead with -fno-ms-extensions.

#include <stdio.h>

union ab {
struct {
int a;
} a_st;
struct {
int b;
} b_st;
};

typedef struct d {
union ab;
struct {
int c;
} c_st;

} d_st;

int main(void) {
d_st x;
printf("%zu\n", sizeof(d_st));
x.a_st.a = 1;
x.b_st.b = 2;
x.c_st.c = 3;
printf("a_st = %d\n", x.a_st.a);
printf("b_st = %d\n", x.b_st.b);
printf("c_st = %d\n", x.c_st.c);
return 0;
}

C global anonymous struct / union

It's simply not possible to do like that. A global variable (like the ones you declare in the header file) are not the same as members of an anonymous structure or union, that simply wont work.

And having anonymous structures or unions won't help with pointer arithmetic, the structure will still sit somewhere in memory, and the compiler uses offsets from the structures base-address to find out where the members are. However, since both the base-address and member offsets are know at time of compilation, the compiler will generally be able to generate code to access the member directly, just like any other variable. Look at the generated code if you are unsure.

So skip the nonsense with anonymous structures, define them properly in the header files, and declare variables of those structures in the header files too, while defining those variables in some source file.

So for the header file:

union you_union
{
uint64_t ab;
struct { uint32_t a, b; };
};

extern union your_union your_union;

And in a source file

union your_union your_union;

merge bit fields across anonymous structs in C

The result you are looking for can be obtained by making the structure union instead, with two bit-fields overlapping. The bits "used" by the first bitfield will be marked as "reserved" in the second one:

union S {
struct {
u16 a: 9;
u16 b: 1;
u16 c: 1;
u16 d: 1;
} ;

struct {
u16 reserved: 12; // Number of bits used in the first struct
u16 e: 4;
};
};

Demo

Anonymous structs in C

My question is: Other than it being a bad coding practice, is there any reason why this code is wrong (strictly in a C compiler sense)?

I don't necessarily agree that the code exhibits poor practices. It certainly is valid, as struct types do not need to be declared with tags in C. Struct types without tags cannot be directly referenced outside the declaration in which they appear, but they serve just fine for declaring objects, and they are more commonly used in typedefs and larger structures.

For example, this approach is relatively common:

typedef struct {  // <-- no tag
char *label;
int value;
} struct_one;

struct_one one[2] = {
{ "one", 1 },
{ "two", 2 }
};

After all, if you're not going to refer to the type via a tag anyway, then why introduce one into the tag namespace?

I guess I'm interested in finding the standard reference to give
Microsoft if they try to tell me that this bug is my fault (though I
believe that either their compiler or debugger must be broken, since
the code should either not compile/link or should debug correctly,
right?)

Well, there's this piece of C's formal grammar specification:

struct-or-union-specifier:

struct-or-union identifieropt { struct-declaration-list }

struct-or-union identifier

The opt subscript indicates that the identifier (the tag in this case) is optional in struct and union definitions. It is required for forward declarations of struct and union types, but that's not your case. It follows from the rest of the formal grammar that any struct-or-union-specifier can be used to declare an object of that type or a type derived from it, such as an array type.

Moreover -- and I suspect that this is where the MS debugger falls down -- there is no limitation on the number of structure or union types declared without a tag. Being without a tag is not the same thing as having an empty tag.

Ultimately, compilers (including Microsoft's) accept it, and I am fairly confident that you will find even the MSVC-compiled version to behave as expected, modulo program bugs. That the debugger does not understand the variables' types correctly is definitely a bug in the debugger.

Do be aware, however, that Microsoft has historically shown little inclination to provide a conforming C compiler, theirs never having conformed to any version of the C language. Most often, it is syntax and features that diverge from C++ that are poorly or un-supported by MS, as C++ has always been their focus.

Have anonymous structs and unions in C11 been incorrectly described?

TL;DR

This looks like a wording issue, they should not overlap.

Details

This is covered in Defect Report (DR) 499 which asks:

Given the following code:

union U {   
struct {
char B1;
char B2;
char B3;
char B4;
};
int word;
} u;

Does the storage of B1, B2, B3 and B4 overlap?

According to 6.7.2.1#13, the members should overlap in storage as they
become members of 'union U'.

At least one implementation (GCC) seems
to NOT consider them to be overlapping.

At least one implementation
(IBM's XL LE AIX) considers them to be overlapping as the standard
currently states.

And the committees response was:

The storage does not overlap.

A related issue is to be found in DR 502 and both may be resolved with coordinated wording changes.

and (emphasis added)

Change §6.7.2.1 p13 from:

An unnamed member of structure type with no tag is called an anonymous structure; an unnamed member of union type with no tag is
called an anonymous union. The members of an anonymous structure or
union are considered to be members of the containing structure or
union. This applies recursively if the containing structure or union
is also anonymous.

to:

An unnamed member of structure type with no tag is called an anonymous structure; an unnamed member of union type with no tag is
called an anonymous union. The names of members of an anonymous
structure or union are added to the name space of the containing
structure or union. This applies recursively if the containing
structure or union is also anonymous.

would adequately resolve the issue.



Related Topics



Leave a reply



Submit