The below code can be compiled successfully in Visual C++. I like it and it is sweet!
#include <stdio.h>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4201)
#pragma pack(push,1)
#define PACKED
#else
#define PACKED __attribute__ ((__packed__))
#endif
union A {
struct {
int a:1;
int b:2;
int c1:29;
}PACKED;
struct {
int a:1;
int b:2;
int c2:28;
int d:1;
}PACKED;
int val;
}PACKED;
#ifdef _MSC_VER
#pragma pack(pop)
#pragma warning(pop)
#endif
#undef PACKED
int main(){
A test;
test.val = 0x1078FFF7;
printf("sizeof(A): %d, test.a: %d.\n", sizeof(A), test.a);
return -1;
}
Output with the file built with MSC:
sizeof(A): 4, test.a: -1.
But in GCC, including the latest gcc-7, it failed to be compiled, :(
struct.cpp:13:15: error: redeclaration of ‘signed char:1 A::<unnamed struct>::a’
int a:1;
^
struct.cpp:7:15: note: previous declaration ‘signed char:1 A::<unnamed struct>::a’
int a:1;
^
struct.cpp:14:15: error: redeclaration of ‘signed char:2 A::<unnamed struct>::b’
int b:2;
^
struct.cpp:8:15: note: previous declaration ‘signed char:2 A::<unnamed struct>::b’
int b:2;
^
Is it a bug in GCC?
Thanks for your comments, I just understood this question may be invalid for C; But for C++ part, I still have the concern. Personally I like Visual C++ compile behavior, it can save tons of code in my scenario
6.7.2.1 Structure and union specifiers says:
An unnamed member whose type specifier is a structure specifier with no tag is called an anonymous structure; an unnamed member whose type specifier is a union specifier 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.
(emphasis mine)
So based on that, it's essentially as if you had:
union A
{
int a:1;
int b:2;
int c1:29;
int a:1;
int b:2;
int c2:28;
int d:1;
int val;
};
which would obviosuly be invalid and gcc correctly issues diagnostics.
It's not a bug in GCC.
The language standard does not allow this. But Visual C++ does, if anything to allow compilation of Windows headers. In fact, if you don't want to use a Microsoft compiler to compile Windows headers then you need to use
#define NONAMELESSUNION
before #include <windows.h>
. It probably seemed like a good idea at the time.
Reference: What are anonymous structs, and more importantly, how do I tell windows.h to stop using them?
It has been discussed in P.P.'s answer and the comments, that what you want isn't correct and GCC behaves correctly. But an easy workaround which fulfills your need might be that you rename a
and b
in the second struct:
union A {
struct {
int a:1;
int b:2;
int c1:29;
}PACKED;
struct {
int unused_name_a:1;
int unused_name_b:2;
int c2:28;
int d:1;
}PACKED;
int val;
}PACKED;
This worked for me in GCC and Clang and should still allow your trick to work nicely.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With