检查联合实例之间的相等性的正确方法是什么?

我有一个multithreading应用程序,它将数据存储为以下联合的实例数组

union unMember { float fData; unsigned int uiData; }; 

存储此数组的对象知道联合中的数据是什么类型,因此在检索正确类型时我不会遇到UB问题。 但是在程序的其他部分,我需要测试这两个联合实例之间的相等性,并且在这部分代码中,真正的内部数据类型是未知的。 结果是我无法使用这种方法测试联合的相等性

  unMember un1; unMember un2; if (un1 == un2) { // do stuff } 

因为我得到编译错误。 因此,我只是比较联合的浮动部分

  if (un1.fData == un2.fData) { // compiles but is it valid? } 

现在考虑到我已经读过它是UB访问一个联盟的任何部分,而不是最后写的那部分(这是繁琐的写,但我可以想到没有更明确的方式来说这个)我想知道代码是否以上是检查我的联合实例的相等性的有效方法?

这让我意识到内部我不知道工会是如何运作的。 我假设数据只是存储为一个位模式,你可以根据你所喜欢的方式解释它,具体取决于联合中列出的类型。 如果不是这种情况,那么测试两个联合实例的相等性的安全/正确方法是什么?

最后,我的应用程序是用C ++编写的,但我意识到工会也是C的一部分,所以两种语言如何对待它们有什么不同?

通常,您需要预先添加当前联合类型的某种指示符:

 struct myData { int dataType; union { ... } u; } 

然后:

 if (un1.dataType != un2.dataType) return (1 == 0); switch(un1.dataType) { case TYPE_1: return (un1.u.type1 == un2.u.type1); case TYPE_2: ... } 

无论如何,语法

 if (un1.fData == un2.fData) { // compiles but is it valid? } 

哪个编译并且有效,可能由于两个原因而无法工作。 一个是,正如你所说,也许un2包含一个整数而不是一个浮点。 但在这种情况下,相等测试通常会失败。 第二个是两个结构都有一个浮点,它们代表相同的数字,并有轻微的机器错误。 然后测试将告诉你数字是不同的(它们是一点一点),而它们的“含义”是相同的。

通常比较浮点数

 if (dabs(f1 - f2) < error) 

避免这个陷阱。

不同的类型可能具有不同的存储长度(两个字节对比四个字节)。

当一个工会成员被写入时,所有保证的是写入的成员是正确的。

如果那时你比较一个不同的成员,你不知道额外的字节将是什么。

测试联合等式的正确方法是有一个包含union和member的结构,它指示当前正在使用的成员,并打开该成员,其中switch的情况处理每个可能的union成员的相等性检查,例如,您必须将使用中的信息与工会一起存储。

例如

 enum test_enum { TEST_ENUM_INT, TEST_ENUM_FLOAT }; union test_union { int test_int; float test_float; }; struct test_struct { enum test_enum te; union test_union tu; }; 

我认为如果你实现了一个类,那将是最安全的。 如果构造不提供特征(在这种情况下自动确定要评估的正确成员),那么构造可能不适合您的需要,您应该使用另一个构造;)这可能是一个自定义类,或者可能是VARIANT如果你使用COM(它基本上是@lserni提出的结构)。

一般来说,你所要求的是不可能的。 只有您设置的变量中的内存才能保证符合您的预期。 另一个记忆基本上是随机的。 但是 ,在您的情况下,您可以比较它,因为一切的大小是相同的。 如果我这样做,我只会比较无符号的整数或做一个memcmp。 这一切都依赖于联盟的所有成员都具有相同的规模。 例如,如果您添加了一个双倍,则所有投注都将关闭。 你可以做到这一点并且在C / C ++中侥幸逃脱,但是它很难维护。 您正在对联合做出假设,并且需要在代码中明确您做出此假设。 未来的维护者可能会破坏它并导致各种难以调试的问题。

最好的办法是在其中包含一个带有类型标志的结构,或者使用像Boost Variant这样的结构 。 当使用这样的东西时,你将会自己进行未来validation并使用未来维护者有机会知道或可以查阅文档的标准代码。

另一个注意事项,你必须在浮点数的情况下通过相等来定义你的意思。 如果你想进行模糊比较,那么你当然需要知道类型。 如果你想进行逐位比较那么这很容易。

在C ++中,不是最后写入成员的成员被认为是未初始化的 (因此读取它们是未定义的行为)。 在C中,它们被认为包含写入的成员的对象表示 ,其可能是或不是有效的对象表示。

那是,

 union U { S x; T y; } u; ux = 0; T t = uy; // C++ - reading uninitialized memory - could crash T t = uy; /* C - reading object representation of ux - could crash */ 

实际上,如果代码与编写指定成员的代码相距足够 ,则C ++读取union未指定成员的行为与C的行为相同,因为编译器生成行为不同的代码的唯一方法是优化读取 – 写作组合。

两种语言中的安全方法(保证不会崩溃)是将内存内容作为char数组进行比较,例如使用memcmp

 union U u1, u2; u1.x = 0; u2.x = 0; memcmp(&u1, &u2, sizeof(union U)); 

然而,这可能不反映工会成员的实际平等; 例如,对于浮点类型,两个NaN can值具有相同的存储器表示并且比较不相等,而-0.00.0 (负和正零)具有不同的存储器表示但是比较相等。 还存在两种类型具有不同大小的问题,或者包含不参与该值的比特填充比特 ,在大多数现代商品平台上不是问题)。 此外,struct类型可以包含用于对齐的填充。