Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are identically named members of unnamed structs in a union an error or a GCC bug?

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

like image 581
ravin.wang Avatar asked Apr 04 '18 08:04

ravin.wang


3 Answers

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.

like image 79
P.P Avatar answered Oct 27 '22 21:10

P.P


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?

like image 43
Bathsheba Avatar answered Oct 27 '22 21:10

Bathsheba


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.

like image 1
johannes Avatar answered Oct 27 '22 22:10

johannes