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

时间:2012-08-30 11:24:24

标签: c++ c unions

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

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

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

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

因为我遇到了编译错误。因此,我只是简单地比较联合的浮动部分

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

现在因为我已经读到它是UB访问,这是不是最后一次写入(即麻烦地写,但我想不出更为清晰的方式来表达这个)我想知道的部分工会的任何部分如果上面的代码是检查我的联合实例相等的有效方法吗?

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

最后,我的应用程序是用C ++编写的,但我发现工会也是C的一部分,所以这两种语言对它们的处理方式有什么不同吗?

5 个答案:

答案 0 :(得分:6)

通常,您需要在当前联合类型之前添加某种指标:

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)

避免这个陷阱。

答案 1 :(得分:0)

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

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

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

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

E.g。

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;
};

答案 2 :(得分:0)

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

即,

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

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

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

union U u1, u2;
u1.x = 0;
u2.x = 0;

memcmp(&u1, &u2, sizeof(union U));

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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

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