问题 我们可以在联合中使用指针吗?


如果没有原因?结构的使用?


8351
2018-01-20 14:35


起源



答案:


您可以在联合中使用任何数据类型,没有任何限制。

至于结构在结构上的使用,结构在存储器中依次布置它们的数据。这意味着它们的所有子组件都是分开的。

另一方面,工会对所有子组件使用相同的内存,因此一次只能存在一个。

例如:

                                 +-----+-----+
struct { int a; float b }  gives |  a  |  b  |
                                 +-----+-----+
                                    ^     ^
                                    |     |
                 memory location:  150   154
                                    |
                                    V
                                 +-----+
union { int a; float b }  gives  |  a  |
                                 |  b  |
                                 +-----+

结构用于“对象”由其他对象组成的位置,例如由两个整数组成的点对象,即x和y坐标:

typedef struct {
    int x;           // x and y are separate
    int y;
} tPoint;

联合通常用于以下情况:对象可以是众多事物中的一种,但一次只能有一种,例如无类型存储系统:

typedef enum { STR, INT } tType;
typedef struct {
    tType typ;          // typ is separate.
    union {
        int ival;       // ival and sval occupy same memory.
        char *sval;
    }
} tVal;

它们对于节省内存非常有用,尽管现在这种情况越来越少(除了像嵌入式系统这样的低级工作),所以你看不到很多。


11
2018-01-20 14:52



工会不是为了挽救记忆:他们表达了一个 总和类型,即。一种持有例如的类型。 或 一个浮子 要么 一个int。工会很难使用,而且非常有限。请考虑使用 boost::variant 代替。 - Alexandre C.
我恐怕不得不同意所有这些,@亚历山大:-)他们 是 (即使是现在)对于节省内存非常有用,特别是在小型嵌入式世界中。如果您了解底层布局(包括实现定义的缺陷)并且直到有人端口提升到C(检查标签),它们并不难使用,变体类型不会有太大用处:-) - paxdiablo
我忘记了1)C 2)人们有时需要靠近硬件。不过,我的观点是,联合(在C ++中)不应该用来表示和类型。 - Alexandre C.
@AlexandreC。标记的工会又出了什么问题?你的支持论点似乎有问题,反驳得很好。你提供进一步的理由吗?我认为“限制”无关紧要,因为构造(标记的联合)具有充分的工作能力,而“难度”显然是主观的。具有讽刺意味的是,我认为在等待时保持流动 boost 比简单地清楚地思考我编程的内容要困难得多,也要耗费时间。 - MickLH


答案:


您可以在联合中使用任何数据类型,没有任何限制。

至于结构在结构上的使用,结构在存储器中依次布置它们的数据。这意味着它们的所有子组件都是分开的。

另一方面,工会对所有子组件使用相同的内存,因此一次只能存在一个。

例如:

                                 +-----+-----+
struct { int a; float b }  gives |  a  |  b  |
                                 +-----+-----+
                                    ^     ^
                                    |     |
                 memory location:  150   154
                                    |
                                    V
                                 +-----+
union { int a; float b }  gives  |  a  |
                                 |  b  |
                                 +-----+

结构用于“对象”由其他对象组成的位置,例如由两个整数组成的点对象,即x和y坐标:

typedef struct {
    int x;           // x and y are separate
    int y;
} tPoint;

联合通常用于以下情况:对象可以是众多事物中的一种,但一次只能有一种,例如无类型存储系统:

typedef enum { STR, INT } tType;
typedef struct {
    tType typ;          // typ is separate.
    union {
        int ival;       // ival and sval occupy same memory.
        char *sval;
    }
} tVal;

它们对于节省内存非常有用,尽管现在这种情况越来越少(除了像嵌入式系统这样的低级工作),所以你看不到很多。


11
2018-01-20 14:52



工会不是为了挽救记忆:他们表达了一个 总和类型,即。一种持有例如的类型。 或 一个浮子 要么 一个int。工会很难使用,而且非常有限。请考虑使用 boost::variant 代替。 - Alexandre C.
我恐怕不得不同意所有这些,@亚历山大:-)他们 是 (即使是现在)对于节省内存非常有用,特别是在小型嵌入式世界中。如果您了解底层布局(包括实现定义的缺陷)并且直到有人端口提升到C(检查标签),它们并不难使用,变体类型不会有太大用处:-) - paxdiablo
我忘记了1)C 2)人们有时需要靠近硬件。不过,我的观点是,联合(在C ++中)不应该用来表示和类型。 - Alexandre C.
@AlexandreC。标记的工会又出了什么问题?你的支持论点似乎有问题,反驳得很好。你提供进一步的理由吗?我认为“限制”无关紧要,因为构造(标记的联合)具有充分的工作能力,而“难度”显然是主观的。具有讽刺意味的是,我认为在等待时保持流动 boost 比简单地清楚地思考我编程的内容要困难得多,也要耗费时间。 - MickLH


那么,根据ISO / IEC 9899:TC3(C99标准):

联合类型描述了一组重叠的非成员对象,每个成员对象   它具有可选的指定名称和可能不同的类型。

简而言之,union成员的内存空间重叠,您为union成员提供的名称允许您在该位置读取内存大小。考虑:

#include <stdio.h>
#include <stdint.h>

typedef union
{
    struct
    {
        uint8_t a;
        uint8_t b;
        uint8_t c;
        uint8_t d;
    };
    uint32_t x;
} somenewtype;

typedef union
{
    uint32_t* p;
    uint8_t* q;
} somepointer;

int main(int argc, char** argv)
{
    uint32_t r;
    uint8_t s;
    somenewtype z;
    somepointer p;
    r = 0x11223344; s = 0x11;
    z.x = 0x11223344;
    p.p = &r;
    p.q = &s;
    printf("%x%x%x%x\n", z.d, z.c, z.b, z.a);
    printf("%x %x\n", *(p.p), *(p.q));
}

在第一个printf中,我们正在做的是打印出32位整数的8位部分。希望当然没有填充匿名结构。

在第二个printf?我不得不单步使用gdb来理解,但我做到了:

p.p = (uint32_t *) 0x7fffffffde5c;
p.q =  (uint8_t *) 0x7fffffffde5b "\021D3\"\021P\337\377\377\377\177";
p.p = (uint32_t *) 0x7fffffffde5b;

当然,指针大小都相同,所以分配 p.q 覆盖地址 p.p。我强烈怀疑将32位整数的地址解析为8位指针正在打印“该位置的任何内容+大小为32位”,这对我来说恰好是偶然的 22334411。但我怀疑,在那一点上,行为是不确定的。

无论如何,这个小练习的目的是告诉你:

  • 联合可用于通过不同的“类型”修饰符访问相同的内存位置。
  • 你必须小心你所做的事情并理解你正在使用的基础类型。如果你要使用指针,请注意他们的修改,因为你可能会开始指出谁知道什么。

我应该指出,我可以看到实际用途 somenewtype 但不是 somepointer  - 这是一个人为的例子,我很确定会破产。


4
2018-01-20 15:17