“struct { ... }”和“struct { union { struct { ... } } }”有什么区别?

5

DISK_DETECTION_INFO为什么要定义成这样?

typedef struct _DISK_DETECTION_INFO {
  DWORD          SizeOfDetectInfo;
  DETECTION_TYPE DetectionType;
  union {
    struct {
      DISK_INT13_INFO    Int13;
      DISK_EX_INT13_INFO ExInt13;
    };
  };
} DISK_DETECTION_INFO, *PDISK_DETECTION_INFO;

替代

typedef struct _DISK_DETECTION_INFO {
  DWORD          SizeOfDetectInfo;
  DETECTION_TYPE DetectionType;
  DISK_INT13_INFO    Int13;
  DISK_EX_INT13_INFO ExInt13;
} DISK_DETECTION_INFO, *PDISK_DETECTION_INFO;

这段代码是不是我过分解读了?

该联合体仅包含一个成员,因此我认为没有任何区别。 - user623879
对齐方面有什么影响吗? - David Heffernan
我怀疑是编写 DISK_DETECTION_INFO 结构的人犯了一个简单的错误。我猜测 DISK_INT13_INFODISK_EX_INT13_INFO 结构应该是联合体的成员,因为只有其中之一被使用。这个错误造成的唯一伤害就是浪费了一点空间。 - Michael Burr
4个回答

6

可以说,这是一个错误。然而,有可能我们只得到了该结构的公共定义。在内部(当Windows内核使用时),它可能被定义为:

typedef struct _DISK_DETECTION_INFO {
  DWORD          SizeOfDetectInfo;
  DETECTION_TYPE DetectionType;
  union {
    struct {
      DISK_INT13_INFO    Int13;
      DISK_EX_INT13_INFO ExInt13;
    };
    DISK_INTERNAL_INFO   Private; // Used internally, when DetectionType = -1
  };
} DISK_DETECTION_INFO, *PDISK_DETECTION_INFO;

我不会把这个代码称为可维护的、安全的或可移植的,但是这是可能的

DISK_INTERNAL_INFO甚至可以超过匿名struct的大小 - 前提是用户从未自己实例化对象,这种技术甚至可以被认为是将额外数据隐藏在用户结构中但仍然保留它的有用方法。他们永远不会“看到”匿名struct之后的内容。


0

行为之间有一个非常具体的差别:在第一种情况下,当初始化 DISK_DETECTION_INFO 时,既没有初始化 Int13 也没有初始化 ExtInt13。而在后一种情况下,所有四个字段都被初始化。

MSVC 有许多古怪的扩展,包括在结构体末尾使用可变大小数组,这可能会导致自动初始化行为不理想。

编辑:关于“初始化”:

假设结构体是

typedef struct _foo {
  int bar;
  union {
    struct {
      int baz;
      int wee;
    };
  };
} foo;

因此,编写foo x = {1;}不会为baz或wee分配值(它们在技术上是不确定的)。如果该结构体是

typedef struct _foo {
  int bar;
  int baz;
  int wee;
} foo;

然后,编写foo x = { 1; }会将baz=0和wee=0赋值


结构体末尾的可变大小数组并非仅限于MSVC,这里也没有这样的东西。无论如何,您从未初始化过其中任何一个,系统会为您返回一个。 - David Heffernan
@David,那是你的观点。在DDK方面有开发人员,他们实际上初始化这些结构体。系统并不会像魔术一样从空气中创造它们(尽管如果可以的话,那将很酷)。 - Foo Bah
我认为你的评论更适合通过它们的构造函数来初始化ADT - 因为它们占用相同的内存空间,所以不可能隐式地初始化两者。 - ta.speot.is
@todda.speot.is,codepad使用gcc还是msvc?它是MSVC扩展,而gcc的-fms-extensions与msvc不完全兼容。尽管如此,我会尝试找到一个链接。 - Foo Bah
@todda.speot.is 我的谷歌功夫不行,但通过一些搜索我发现:“在初始化期间,未命名结构体或联合体成员会被忽略。” http://techpubs.sgi.com/library/dynaweb_docs/0650/SGI_Developer/books/CLanguageRef/sgi_html/ch07.html 我记得几年前在MSDN文档中读到了一个更详尽的讨论,但我得找出那些旧的光盘。 - Foo Bah
显示剩余3条评论

0

我认为有合理的证据表明,当原始DISK_DETECTION_INFO结构定义被编写时,某人犯了一个简单的错误。这个错误逃脱到了公众视野中,所以无法再进行修复。

头文件中的定义如下:

typedef struct _DISK_DETECTION_INFO {
        DWORD SizeOfDetectInfo;
        DETECTION_TYPE DetectionType;
        union {
                struct {

                        //
                        // If DetectionType == DETECTION_INT13 then we have just the Int13
                        // information.
                        //

                        DISK_INT13_INFO Int13;

                        //
                        // If DetectionType == DETECTION_EX_INT13, then we have the
                        // extended int 13 information.
                        //

                        DISK_EX_INT13_INFO ExInt13;     // If DetectionType == DetectExInt13
                } DUMMYSTRUCTNAME;
        } DUMMYUNIONNAME;
} DISK_DETECTION_INFO, *PDISK_DETECTION_INFO;

文档中写道:

如果DetectionType是DetectInt13,则联合体是一个DISK_INT13_INFO结构。

如果DetectionType是DetectExInt13,则联合体是一个DISK_EX_INT13_INFO结构。

因此,由于它们是互斥的,所以很有可能最初的意图是将DISK_INT13_INFO和DISK_EX_INT13_INFO放在一个联合体中。


-4

联合通常用于在一组字段具有互斥访问时节省空间。也就是说,当一个字段处于活动状态时,其他字段必须不活动。与为所有字段分配空间相反,只为最大的字段分配空间。该空间可与其他字段交替使用。如链接中所述,根据DetectionType字段,Int13或ExInt13处于活动状态。两者都使用相同的分配空间。


2
实际上,Int13和ExInt13都在OP代码的结构体中,因此它们都有自己的地址和空间。 - K-ballo
@K-ballo:为什么?Int13ExInt13在同一个结构体中,因此它们不会重叠。 - phoxis
1
@phoxis...这就是为什么它们“有自己的地址”的原因。 - ta.speot.is
1
@phoxis:那正是我的观点,我猜我没有表达正确吗?应该是地址。 - K-ballo
我误读了评论,对此感到抱歉,我们正在讨论同一个问题。 - phoxis
看起来我错过了联合内部结构,因为通常的做法就像我的答案。也许API设计有问题.... - LeleDumbo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接