代码之家  ›  专栏  ›  技术社区  ›  user978122

C工会背后的意义是什么?

  •  7
  • user978122  · 技术社区  · 11 年前

    我正在阅读奥莱利的《实用C编程》一书,并阅读了K&关于C编程语言的R书,我真的很难理解联合背后的概念。

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

    书中提到,它用于通信,需要设置相同大小的标志;在谷歌搜索的网站上,它可以消除奇怪大小的内存块。。。但它在现代非嵌入式存储空间中有什么用处吗?

    你能用它和CPU寄存器做些什么巧妙的事情吗?这只是对早期编程时代的保留吗?还是像臭名昭著的goto一样,它仍然有一些强大的用途(可能在内存空间紧张的情况下),值得保留?

    5 回复  |  直到 11 年前
        1
  •  5
  •   Mario    11 年前

    好吧,你几乎回答了你的问题:记忆。 在过去,内存相当低,甚至节省几千字节也很有用。

    但即使在今天,工会也会发挥作用。例如,如果您想实现某种 variant 数据类型。做到这一点的最好方法是使用工会。

    这听起来不算多,但假设您想使用一个变量来存储4个字符串(如ID)或4个字节的数字(可能是一些散列,也可能只是一个数字)。

    如果你使用经典 struct ,这将是8个字节长(至少,如果你不走运的话,还有填充字节)。使用 union 它只有4个字节。因此,您节省了50%的内存,例如,这并不算多,但想象一下有一百万个这样的内存。

    虽然可以通过强制转换或子类化联合来实现类似的事情,但这仍然是最简单的方法。

        2
  •  1
  •   Patashu    11 年前

    并集的一个用途是让两个变量占据相同的空间,结构中的第二个变量决定要读取的数据类型。

    例如,您可以有一个布尔值“isDouble”和一个既有double又有long的并集“doubleOrLong”。如果isDouble==true,则将并集解释为double,否则将其解释为long。

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

    现在你真的不需要这个,因为内存很便宜,但在嵌入式系统中它有它的用途。

        3
  •  0
  •   Anthony Atmaram    11 年前

    Windows API大量使用联合。 LARGE_INTEGER 是这种用法的一个例子。基本上,如果编译器支持64位整数,请使用 QuadPart 成员否则,手动设置低DWORD和高DWORD。

        4
  •  0
  •   Alan    11 年前

    这并不是真正的搁置,因为C语言创建于1972年,当时内存是一个真正的问题。

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

    因此,当选择使用C时,您仍然希望利用它的优势,包括内存空间效率。为此,欧盟运作得很好;允许您具有一定程度的类型安全性,同时实现可用的最小内存足迹。

        5
  •  0
  •   kibibu laffuste    11 年前

    我见过它被使用的一个地方是在末日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 ,将其投射到一个指向长的指针(即地震天数中的32位整数),并将其取消绑定到 i 。然后它会做一些令人难以置信的奇怪的事情,反之亦然。

    这样做有两个缺点。一个是复杂的地址、强制转换、取消引用过程迫使 y 从内存中读取,而不是从寄存器中读取 1. ,回来的路上也是如此。然而,在Quake时代的计算机上,浮点寄存器和整数寄存器是完全分开的,所以你几乎必须将其推到内存中再推回来处理这个限制。

    第二个问题是,至少在C++中,做这样的铸造是非常不受欢迎的,即使是在做像这个函数这样相当于巫毒的事情时。我相信还有更令人信服的论点,但我不确定它们是什么:)

    因此,在《末日3》中,id在他们的新实现中包含了以下比特(使用了一组不同的比特处理,但有类似的想法):

    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机器上,可以通过单个寄存器进行访问;我不确定在实践中是否有编译器会这样做。在我看来,这仍然比早期《地震》版本中的选角游戏更干净。


    1. -忽略“足够高级的编译器”参数