【问题标题】:Can we use pointer in union?我们可以在联合中使用指针吗?
【发布时间】:2011-06-12 12:17:54
【问题描述】:

如果没有,为什么?使用联合而不是结构?

【问题讨论】:

    标签: c embedded unions


    【解决方案1】:

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

    关于在结构上使用联合,结构在内存中按顺序排列它们的数据。这意味着它们的所有子组件都是独立的。

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

    例如:

                                     +-----+-----+
    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;
    

    它们对于节省内存很有用,尽管现在这点越来越少(除了在嵌入式系统等低级工作中),所以你看不到很多。

    【讨论】:

    • 联合不是为了节省内存:它们表示一个sum类型,即。一种类型,例如。 要么浮点数整数。工会很难使用,而且非常有限。请考虑改用boost::variant
    • 恐怕我不得不不同意几乎所有这些,@Alexandre :-) 它们(即使是现在)对于节省内存特别是在小型 -嵌入式世界。如果您了解底层布局(包括实现定义的弱点),它们并不难使用,并且在有人将提升到 C (检查标签)之前,变体类型不会有太大用处:-)
    • 我忘记了 1) 是 C 2) 人们有时需要靠近硬件。尽管如此,我的观点是联合(在 C++ 中)不应该用于表示和类型。
    • @AlexandreC。再次标记工会有什么问题?您的支持论点似乎有问题,并且已被很好地反驳。你提供任何进一步的理由吗?我认为“限制”无关紧要,因为构造(标记的联合)为这项工作做好了充分的准备,而“困难”显然是主观的。具有讽刺意味的是,我认为在等待 boost 时保持流程比简单地思考我正在编程的内容要困难得多,也更耗时。
    【解决方案2】:

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

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

    简而言之,联合成员的内存空间是重叠的,你给联合成员起的名字可以让你读取那个位置的内存大小。考虑:

    #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 - 这是一个人为的例子,我很确定会破坏。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-05
      • 1970-01-01
      • 1970-01-01
      • 2019-09-08
      • 2011-01-17
      • 1970-01-01
      • 2010-09-20
      • 2019-02-06
      相关资源
      最近更新 更多