联合与结构有什么不同?其他语言有类似的结构吗?
可能重复的问题:
C语言中结构体和联合体的区别
我看到这段关于C语言中联合体的代码:
union time
{
long simpleDate;
double perciseDate;
} mytime;
在C语言中,联合体和结构体有什么区别?什么时候会用到联合体,它有什么好处?在Java、C++和/或Python中有类似的东西吗?
7 个回答
联合体让你可以用不同的方式来理解同一个内存位置(也就是原始的二进制值)。
我曾经用过的一个例子是访问一个 uint32(无符号32位整数)的每一个字节。
union {
uint32 int;
char bytes[4];
} uint_bytes;
联合体的好处在于,你可以用多种方式来访问同一块内存的不同部分。
联合体的大小等于联合体中最大类型的大小。
一个联合体的表现就像它的所有成员都在内存中的同一个位置上,彼此重叠。
这在你想表示某种“通用”值,或者一个可以是多种类型中的任何一种值时非常有用。因为这些字段是重叠的,所以你只能在知道某个字段已经被初始化的情况下合法地访问它。不过,由于C语言并不会检查这一点,而且许多编译器生成的代码也允许这样做,所以这成为了一种常见的技巧,用于进行一些……有趣的类型转换,比如:
union {
int integer;
float real;
} convert;
convert.real = 3.14;
printf("The float %f interpreted as an integer is %08x", convert.real, convert.integer);
对于更规范的用法,你可以跟踪最后存储在联合体中的内容,可能看起来像这样:
typedef enum { INTEGER = 0, REAL, BOOLEAN, STRING } ValueType;
typedef struct {
ValueType type;
union {
int integer;
float real;
char boolean;
char *string;
} x;
} Value;
在这里,注意联合体实际上是外部结构体中的一个字段,叫做Value
。访问方式可能是这样的:
void value_set_integer(Value *value, int x)
{
value->type = INTEGER;
value->x.integer = x;
}
这段代码记录了联合体当前的内容是一个整数,并存储了给定的值。比如说,一个打印Value
的函数可以检查type
成员,并做出正确的处理:
void value_print(const Value *value)
{
switch(value->type)
{
case INTEGER:
printf("%d\n", value->x.integer);
break;
case REAL:
printf("%g\n", value->x.real);
break;
/* ... and so on ... */
}
}
在Java中没有类似的东西。C++作为C的一个“超集”,具备相同的功能。它甚至在C的实现上更进一步,允许使用匿名联合体。在C++中,上面的代码可以在不命名内部联合体(x
)的情况下编写,这样代码会简洁很多。
在你的例子中,当我分配时间的时候:
int main()
{
time t;
}
编译器可以把&t这个内存地址看作是一个长整型(long):
t.simpleDate;
或者把它看作是一个双精度浮点型(double):
t.perciseDate;
所以如果t的内存中的原始十六进制值看起来像这样:
0x12345678;
这个值可以根据访问的方式被“解析”为双精度浮点型或长整型。因此,要想让它有用,你必须准确知道长整型和双精度浮点型在内存中是如何被打包和格式化的。例如,长整型是一个二进制补码的有符号整数,你可以在这里了解更多。你也可以在这里学习双精度浮点型是如何以二进制格式表示的。
而结构体(struct)只是把不同的变量组合在一起,形成一个内存块,变量之间有明确的地址间隔。
(注意你的例子可能有风险,因为sizeof(long)可能是32位,而sizeof(double)总是64位)
联合体(union)通常在你想要一个“原始”表示(比如字符数组)和一个“消息”表示时使用。例如,一个要通过套接字发送的消息:
struct Msg
{
int msgType;
double Val1;
double Val2;
}; // assuming packing on 32-bit boundary
union
{
Msg msg;
unsigned char msgAsBinary[20];
};
希望这些信息对你有帮助。