如何在无名称的结构体中嵌套使用C联合体

28

我正在开发所谓的Hotspot开源项目,看到实现时我发现了一个非常棘手的嵌套联合在结构体中,看起来像这样:

typedef struct RC_model_t_st
{
    union
    {
        struct block_model_t_st *block;
        struct grid_model_t_st *grid;
    };
    /* block model or grid model    */
    int type;
    thermal_config_t *config;
}RC_model_t;

据我所知,在C/C++中,该联合是无法访问的。那么,有人如何利用以这种方式声明的联合,并且出于什么目的?

谢谢!


相关:https://dev59.com/BHI-5IYBdhLWcg3wMFa0 - zch
6个回答

39

这是一个匿名联合体。 在C++中,根据 [class.union] 的第5段:

为了进行名称查找,在匿名联合定义之后, 匿名联合的成员被认为已经在声明匿名联合的作用域中定义了

这意味着您可以像访问 RC_model_t_st 的成员一样访问它的成员。


好的。如果我可以将它们作为结构体成员访问,我不明白为什么有人会将它们放在匿名联合里? - zwx
6
因为它们仍然是联合体 - 它们占用相同的存储空间。 - Angew is no longer proud of SO
对于每个变量,您都会分配n字节的内存空间。现在,变量类型仅定义了要解释多少空间以及该空间的内容如何解释。使用联合时,变量使用最大类型的空间,解释取决于您访问的成员。 - Atmocreations

11

在确认并尝试之前:

该联合体本身不可访问,但其成员是可以访问的。

因此,您应该能够引用obj.blockobj.grid


1
你的意思是obj是RC_model_t类型吗? - zwx

9
为了阐述由Angew引用有关匿名联合和结构的标准的答案,我想提供一份C源代码示例和该示例生成的输出,展示在由struct和union组成的匿名struct和union组件内如何分配值。
Angew引用的标准是:
“为了进行名称查找,在匿名联合定义之后,匿名联合的成员被认为是在声明匿名联合所在的范围中定义的。”
以下是由命名和匿名struct和union组成的struct的源代码。这里使用的是Visual Studio 2005,并且使用#pragma (pack, 1)将所有内容对齐到char边界,以便没有内存空洞。还定义了一个简单的C预处理器宏,使输出更易于阅读和编码。
typedef unsigned char UCHAR;

// use of Microsoft Visual Studio pragma to force char alignment for the struct.
#pragma pack(push, 1)
const struct {
    union {
        const UCHAR myArray[];  // this array shares memory with struct following
        struct {
            const UCHAR iOne;
            const UCHAR iTwo;
            const UCHAR iThree;
        };  // anonymous struct accessed by specifying Things.
    };      // anonymous union accessed by specifying Things.
//  const UCHAR myArray[];   // will cause error - "error C2020: 'myArray' : 'struct' member redefinition"
    union {
        const UCHAR myArray[];  // this array shares memory with struct following
        struct {
            const UCHAR iOne;
            const UCHAR iTwo;
            const UCHAR iThree;
        } s;    // named struct accessed by specifying Things.u.s
    } u;        // named union accessed by specifying Things.u
} Things = {1, 2, 4, 8, 9, 10, 22, 23, 24, 25};
#pragma pack(pop)

// a little helper macro to make the output easier to code.
#define PRINTF_VAL(x) printf ("%s %d \n", #x, x)

int itSelf (UCHAR iMask)
{
    int iMatch = -1;

    int jj = 0;
    jj = Things.myArray[0]; PRINTF_VAL(Things.myArray[0]);
    jj = Things.myArray[1]; PRINTF_VAL(Things.myArray[1]);
    jj = Things.myArray[2]; PRINTF_VAL(Things.myArray[2]);
    jj = Things.myArray[3]; PRINTF_VAL(Things.myArray[3]);
    jj = Things.myArray[4]; PRINTF_VAL(Things.myArray[4]);
    jj = Things.iOne; PRINTF_VAL(Things.iOne);
    jj = Things.iTwo; PRINTF_VAL(Things.iTwo);
    jj = Things.iThree; PRINTF_VAL(Things.iThree);

    jj = Things.u.myArray[0]; PRINTF_VAL(Things.u.myArray[0]);
    jj = Things.u.myArray[1]; PRINTF_VAL(Things.u.myArray[1]);
    jj = Things.u.myArray[2]; PRINTF_VAL(Things.u.myArray[2]);
    jj = Things.u.myArray[3]; PRINTF_VAL(Things.u.myArray[3]);
    jj = Things.u.myArray[4]; PRINTF_VAL(Things.u.myArray[4]);
    jj = Things.u.s.iOne; PRINTF_VAL(Things.u.s.iOne);
    jj = Things.u.s.iTwo; PRINTF_VAL(Things.u.s.iTwo);
    jj = Things.u.s.iThree; PRINTF_VAL(Things.u.s.iThree);

    return iMatch + 1;
}

该函数生成的输出如下所示:
Things.myArray[0] 1
Things.myArray[1] 2
Things.myArray[2] 4
Things.myArray[3] 8
Things.myArray[4] 9
Things.iOne 1
Things.iTwo 2
Things.iThree 4
Things.u.myArray[0] 8
Things.u.myArray[1] 9
Things.u.myArray[2] 10
Things.u.myArray[3] 22
Things.u.myArray[4] 23
Things.u.s.iOne 8
Things.u.s.iTwo 9
Things.u.s.iThree 10

输出显示了使用联合体导致的主要struct Things各个组件之间的重叠部分。您还可以看到匿名structunion的组成部分与命名structunion的组成部分相比如何被引用。
另外,仅仅出于好奇,我尝试在包含const UCHAR myArray[];的匿名union之后添加一个const UCHAR myArray[];的数组定义,以查看会发生什么。编译器报错:error C2020: 'myArray' : 'struct' member redefinition。上面Thingsstruct定义中已经将此项添加注释。但是由于第二次使用const UCHAR myArray[];是在命名union中,因此编译可以通过,因为第二次使用是通过指定union的名称来访问的。

6
这段代码(https://gist.github.com/klange/4042963)展示了如何访问结构体中的匿名联合体。你只需要像访问结构体成员一样访问嵌套联合体的成员即可。
typedef struct {
    union {
        char * company;
        char * school;
        char * project;
    };
    union {
        char * location;
        char * url;
    };
    union {
        char * title;
        char * program;
    };

    time_t started;
    time_t left;

    char * description[];
} thing_t;

typedef thing_t job_t;

job_t yelp = {
    .company  = "Yelp, Inc.",
    .location = "San Francisco, CA",
    .title    = "Software Engineer, i18n",
    .started  = 1339977600,
    .left     = CURRENT,
    .description = {
        "Developed several internal tools and libraries",
        "Provided critical input and design work for Yelp's launch in Japan",
        NULL
    }
};

4

在匿名联合中声明的名称直接使用,就像非成员变量一样。这样做的一个好理由是节约内存。

#include <iostream>

int main(int argc, char **argv) {
   union {
      double first;
      double second;
   };

   first = 10.001;
   second = 3.141592;
   std::cout << first << " " << second << std::endl;

   first = 10.002;
   std::cout << first << " " << second << std::endl;
}

2
如果你输出代码的结果,那将会更有帮助。(尽管我们自己也可以运行它) - Brad Pitt

-2
首先,我想说联合体是一组不同类型的变量集合,就像结构体一样。然而,使用联合体时,你只能在任意一个字段中存储信息。

联合体基本上用于节省内存,其大小等于联合体中最大成员的大小。

要访问联合体的数据字段,请使用点运算符(.),就像访问结构体一样,由@Atmocreations解释过。当给一个成员赋值时,其他成员将被清除,因为它们共享同一块内存。

联合体可能有用的一个例子是

union time    
        {
        long time_in_sec;
        double time_in_mili_sec;
        }mytime;

上面的联合体可以用来存储当前时间(以秒为单位)以保持精确到秒的时间。或者它可以用于保持精确到毫秒的时间。想必有时候你只需要其中一个,而不是两个都要。这个声明应该很熟悉。它与结构定义相同,但关键字是union而不是struct。

更多信息请参见http://msdn.microsoft.com/en-us/library/5dxy4b7b(v=vs.80).aspx


这对我来说是最完整的答案。谢谢! - zwx
@akp:请注意,根据架构的不同,对齐可能会导致错误。 - Atmocreations
3
答案并不精确。问题是关于匿名联合的,而答案是关于有名称的联合。 - Sergey Skopus
2
这并没有回答问题。 - theEpsilon

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