C中的工会背后有什么意义?

时间:2013-03-27 23:56:00

标签: c unions

我正在阅读O'Reilly的“实用C编程”一书,并阅读了关于C编程语言的K& R书籍,我在理解联盟背后的概念时遇到了麻烦。

它们采用构成它们的最大数据类型的大小......最近分配的数据会覆盖其余数据......但为什么不根据需要使用/释放内存?

这本书提到它用于通信,你需要设置相同大小的标志;在google网站上,它可以消除奇数大小的内存块...但它是否适用于现代的非嵌入式内存空间?

你能用它和CPU寄存器做些什么狡猾的事吗?它只是一个早期编程时代的延续吗?或者,它是否像臭名昭着的goto一样,仍然有一些强大的用途(可能在紧凑的记忆空间中),这使它值得保持?

5 个答案:

答案 0 :(得分:5)

好吧,你几乎回答了你的问题:记忆。 在这些日子里,内存相当低,即使节省几千字节也很有用。

但即便在今天,工会也会有用。例如,如果您想要实现某种variant数据类型。最好的方法是使用union。

这听起来不是很多,但我们假设你想要使用一个变量,要么存储4个字符的字符串(如ID),要么存储4个字节的数字(可能是一些哈希值,或者实际上只是一个数字)。

如果你使用经典struct,这将是8个字节长(至少,如果你运气不好,那么也有填充字节)。使用union它只有4个字节。所以你节省了50%的内存,这对于一个实例来说并不是很多,但想象有一百万个。

虽然通过强制转换或子类化来实现类似的东西,但仍然是最简单的方法。

答案 1 :(得分:1)

联合的一种用法是让两个变量占用相同的空间,结构中的第二个变量决定你想要读取它的数据类型。

e.g。你可以有一个布尔型'isDouble'和一个带有double和long的union'doubleOrLong'。如果isDouble == true将union解释为double,则将其解释为long。

联合的另一个用途是访问不同表示形式的数据类型。例如,如果你知道double是如何在内存中布局的,你可以在一个联合中加一个double,将它作为一个不同的数据类型访问,如long,直接访问它的位,它的尾数,它的符号,它的指数,等等。 ,并用它做一些直接的操纵。

现在你并不需要这样,因为内存非常便宜,但在嵌入式系统中它有其用途。

答案 2 :(得分:0)

Windows API充分利用了工会。 LARGE_INTEGER就是这种用法的一个例子。基本上,如果编译器支持64位整数,请使用QuadPart成员;否则,手动设置低DWORD和高DWORD。

答案 3 :(得分:0)

这并不是真正的结果,因为C语言是在1972年创建的,当时记忆是一个真正的问题。

您可以提出这样的论点:在现代的非嵌入式空间中,您可能不希望使用C作为编程语言。如果您选择C作为实现的语言选择,那么您希望利用C的优势:它是高效的,接近金属的,这会产生紧凑,快速的二进制文件。

因此,在选择使用C时,您仍然希望利用它的优势,包括内存空间效率。联盟的工作非常好;允许您具有一定程度的类型安全性,同时强制执行最小的内存占用。

答案 4 :(得分:0)

我见过它的一个地方是在Doom 3 / idTech 4 Fast Inverse Square Root实施中。

对于那些不熟悉这种算法的人来说,它基本上需要将浮点数视为整数。旧的Quake(及更早版本)代码通过以下方式实现:

float y = 2.0f;

// treat the bits of y as an integer
long i  = * ( long * ) &y;

// do some stuff with i

// treat the bits of i as a float
y = * ( float * ) &i;

<子> original source on GitHub

此代码获取浮点数y的地址,将其强制转换为指向long的指针(即Quake天中的32位整数),并将其解析为i。然后它做了一些令人难以置信的奇怪的比特琐事,反之亦然。

这样做有两个缺点。一个是复杂的地址,强制转换,解除引用过程强制从内存中读取y的值,而不是从寄存器 1 中读取,并且在返回途中同样如此。然而,在Quake-era计算机上,浮点和整数寄存器是完全独立的,所以你几乎不得不推送到内存并回来处理这个限制。

第二个问题是,至少在C ++中,做这样的演员是非常不受欢迎的,即使在做这样的伏都教就像这个函数那样。我确信有更有说服力的论点,但我不确定它们是什么:)

所以,在Doom 3中,id在他们的新实现中包含了以下位(它使用了一组不同的bit twiddling,但是有类似的想法):

union _flint {
        dword                   i;
        float                   f;
};

...
union _flint seed;
seed.i = /* look up some tables to get this */;
double r = seed.f; // <- access the bits of seed.i as a floating point number

<子> original source on GitHub

理论上,在SSE2机器上,可以通过单个寄存器访问;我在实践中不确定是否有任何编译器会这样做。在我看来,它仍然比早期Quake版本中的投射游戏更清晰。

<小时/> 1 - 忽略“足够高级的编译器”参数