联合中未命名结构的同名成员是错误还是GCC错误?

rav*_*ang 14 c++ gcc language-lawyer

下面的代码可以在Visual C++中成功编译.我喜欢它,它很甜!

#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;
}
Run Code Online (Sandbox Code Playgroud)

使用MSC构建的文件输出:

sizeof(A): 4, test.a: -1.
Run Code Online (Sandbox Code Playgroud)

但在GCC,包括最新的gcc-7,它未能编译,:(

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;
           ^
Run Code Online (Sandbox Code Playgroud)

这是海湾合作委员会的一个错误吗?


感谢您的评论,我只是理解这个问题可能对C无效; 但对于C++部分,我仍然有顾虑.我个人喜欢Visual C++编译行为,它可以在我的场景中节省大量的代码

P.P*_*.P. 28

6.7.2.1结构和联合说明符说:

类型说明符是没有标记的结构说明符的未命名成员称为匿名结构; 一个未命名的成员,其类型说明符是一个没有标记的联合说明符,称为匿名联合.匿名结构或联合的成员被视为包含结构或联合的成员.如果包含的结构或联合也是匿名的,则递归应用.

(强调我的)

基于此,它基本上就像你有:

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;
};
Run Code Online (Sandbox Code Playgroud)

这显然是无效的,gcc正确地发出诊断信息.

  • OP代码是2个结构的并集,而不是内部结构的所有成员的并集. (6认同)
  • 问题不在于无名结构/联合字段所需的唯一名称,而是您在任何对象的所有字段**的**联合中转换了2个无名结构的**联合的事实. (4认同)
  • 对于C来说,对于C++,匿名结构不是标准结构,但是未命名的是:[匿名/未命名结构](/sf/answers/997368921/). (2认同)
  • @ ravin.wang正如Oliv所说的那样,它在C++中是非标准的.所以无论MSVC做什么都允许这样做也是非标准的(如果不是bug). (2认同)
  • @Frankie_C不仅仅是"2个结构"的联合,而是2*匿名*结构 - 由于所述的原因(如标准所引用的)而成为问题.如果内部结构有标签,则不会出现问题. (2认同)

Bat*_*eba 19

这不是GCC中的错误.

语言标准不允许这样做.但是,如果允许编译Windows头文件,Visual C++会这样做.实际上,如果您不想使用Microsoft编译器来编译Windows标头,那么您需要使用

#define NONAMELESSUNION
Run Code Online (Sandbox Code Playgroud)

之前#include <windows.h>.这在当时似乎是个好主意.

参考:什么是匿名结构,更重要的是,如何告诉windows.h停止使用它们?

  • @ ravin.wang:我不知道这是如何"节省大量精力"的.您正在复制字段,这是容易出错且耗时的.如果这些字段不是联合的一部分,那么不要复制/粘贴它们,而只是将它们从联合中取出. (6认同)
  • @ ravin.wang即使在你的情况下,两个匿名结构也保证具有相同的位模式,但是如何安排各个成员(`a`,`b`,`c1`)是实现定义的.所以你不能说两个结构中的'a`将完全相同. (5认同)

joh*_*nes 1

PP 的回答和评论中已经讨论过,您想要的不正确,而 GCC 的行为正确。但是满足您的需求的一个简单的解决方法可能是您在第二个结构中重命名aand :b

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;
Run Code Online (Sandbox Code Playgroud)

这对我在 GCC 和 Clang 中有用,并且应该仍然可以让你的技巧很好地发挥作用。

  • 使用此代码,同时访问“a”和“d”是非法的,因为它们位于不同的联合成员中。 (3认同)
  • @MPW 没有为联合成员设置新值。给定“A a = some_value;”,你不能合法地说“x = aa;” y = ad;`,尽管类似的东西是一个常用的习语,有自己的名字“unioncast”。然而它违反了严格的别名,因此是 UB。 (3认同)
  • @约翰内斯 嗯。一方面,是的。另一方面,OP 明确询问 C++ 标准合规性。 (3认同)