我正在阅读O'Reilly的“实用C编程”一书,并阅读了关于C编程语言的K& R书籍,我在理解联盟背后的概念时遇到了麻烦。
它们采用构成它们的最大数据类型的大小......最近分配的数据会覆盖其余数据......但为什么不根据需要使用/释放内存?
这本书提到它用于通信,你需要设置相同大小的标志;在google网站上,它可以消除奇数大小的内存块...但它是否适用于现代的非嵌入式内存空间?
你能用它和CPU寄存器做些什么狡猾的事吗?它只是一个早期编程时代的延续吗?或者,它是否像臭名昭着的goto一样,仍然有一些强大的用途(可能在紧凑的记忆空间中),这使它值得保持?
答案 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 - 忽略“足够高级的编译器”参数