在C语言中访问联合成员

4

我有一个关于C语言中的联合(union)的问题。

例如:

typedef struct {
    int a;
     float c;
}Type1;


typedef struct {
    int b;
     char d;
}Type2;

union Select {
    Type1 type1;
    Type2 type2;
};


void main() {
    Select* select;
    //can we access type1 first and then access type2 immediately? like this way:
    select->type1.a;
    select->type2.b;

//在访问类型1之后,立即访问类型2,我们能否获取类型2的值b? //我稍微修改了第一篇文章,因为一开始它是没有意义的。

虽然问题与另一件事有关,但即使在人工示例中初始化指针仍然是有意义的。你的 select 没有指向任何地方。这就是 int main()。是 int,而不是 void - AnT stands with Russia
@AndreyT,为什么他不能定义void main()? - Gil.I
7个回答

4
这是ISO/IEC 9899:1999保证的(请参见此处的草案),6.5.2.3 5节:

为了简化联合使用,提供了一项特殊的保证:如果一个联合包含多个具有共同初始序列的结构体(参见下文),并且如果联合对象当前包含其中之一的结构体,则可以在任何声明联合的完整类型可见的地方检查它们任何一个的共同初始部分。如果对应的成员具有兼容类型(对于位字段,宽度相同)的一个或多个初步成员的序列,则两个结构体共享一个公共初始序列。


2

是的,没错。在您的例子中(忽略未初始化的指针),Select 的任何实例的 type1.atype2.b 值将始终相同。


你所说的“始终如一”是什么意思? - user707549
1
在你的例子中,你写入type1.a的任何值都将是你从type2.b读取的值,反之亦然。 - Skizz

0
在你的例子中,它可以工作,因为两者都是类型int
通常情况下,您需要一个鉴别器来知道哪个联合正在使用。
联合的大小为最大数据类型的大小(如果我记得正确的话),每次设置/检查类型以知道要访问哪种数据类型:
struct myStruct {   
    int type;   
    union Select {   
      Type1 type1;   
      Type2 type2;   
     };  
};  

在访问并使用联合体之前,您应该进行检查以了解如何使用它:

myStruct* aStruct;  
//init pointer
    if(aStruct->type == TYPE1)//TYPE1 is defined to indicate the coresponding type
    {
       //access fields of type1
       aStruct->type1.a = //use it
    }

在这之前你应该已经做了:aStruct->type = TYPE1


我不会说“通常情况下,您需要一个鉴别器来知道哪个联合正在使用。” 联合旨在为相同数据提供不同的视图(对于低级IO编程非常有用)。变体数据类型是后来想到的。 - Skizz
@Skizz:一个联合体是一个变量,它可以在不同的时间持有不同类型和大小的对象。联合体足够大,可以容纳最大的封闭类型。只要使用一致,任何封闭类型都可以使用,即检索的类型必须是最近存储的类型(这是程序员的责任)。引用Kernighan-Richie书中的话:“如果将某些内容存储为一种类型并提取为另一种类型,则结果取决于实现”。老实说,我从未在不使用类型的情况下使用过union。也许你的经验不同。 - Cratylus
我曾经看到它们被用于将位域结构映射到单个整数字段上,例如UART状态寄存器,因此您可以单独读取/写入各个字段或在一次操作中读取/写入所有字段。当然,位位置将依赖于具体实现。我能够理解您的引用所适用的地方——大小端和浮点数/整数联合。 - Skizz
@Skizz:在你提到的具体例子中,我相信位域结构在该平台上的大小与整数字段相同,因此您可以相应地解释它。但这是程序员必须真正知道自己在做什么的情况之一。 - Cratylus

0

是的,你可以随时访问它们两个。基本上,select->type1和select->type2是指向内存中同一位置的指针。程序员的工作就是知道该内存位置中存储的内容,通常通过一个标志来实现:

union Select {
    Type1 type1;
    Type2 type2;
};

struct SelectType {
    bool isType1;
    Select select;
};

int getValue (struct SelectType s){
    if (s.IsType1){
        return s.type1.a;
    } else {
        return s.type2.b;
    }
}

void main() {
    struct SelectType select;
    int value;

    select.type1.a = 5;
    select.isType1 = true;

    select.type2.4 = 5;
    select.isType1 = false;

    value = getValue (select);
}

0

是的,你可以这样做,因为主函数具有联合声明的作用域。

引用自C99标准的最终版本。

以下不是有效的片段(因为联合类型在函数f内不可见)

struct t1 { int m; };
struct t2 { int m; };
int f(struct t1 *p1, struct t2 *p2)
{
    if (p1->m < 0)
        p2->m = -p2->m;
    return p1->m;
}

int g()
{
    union {
        struct t1 s1;
        struct t2 s2;
    } u;
    /* ... */
    return f(&u.s1, &u.s2);
}

0

没问题,我们能做到。在这种情况下,它的值不会改变。这不是错误,但没有意义。顺便说一下,您忘记为指针“select”分配内存了吗?

我非常想帮忙,但我的英语不太好。而且这是我的第一篇帖子。如果我说错了什么,请告诉我。


-1

联合体中的所有成员都驻留在同一内存位置。

通常可以以多种方式访问同一块内存。

例如,您可以定义:

union {

char a[100];

int b[50];

}x;

联合体大小将为100字节,并且从b [0]读取就像同时读取a [0]和a [1]。


2
那些日子里,INT只有16位。 :-) - ott--
@ott,在我工作过的许多嵌入式系统中,int实际上是short int,并且是16位。我知道有些系统可能不同。为避免混淆,我总是定义所有字节(8位)、字(16位)和双字(32位)的typedef,并仅使用它们,而不是使用int,并且始终考虑该系统是否为16位或32位以及其默认是否为无符号或带符号。 - Gil.I

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