出于某些测试目的,我需要比较两个 union 以查看它们是否相同。将所有成员一一比较就够了吗?
union Data {
int i;
float f;
char* str;
} Data;
bool isSame(union Data left, union Data right)
{
return (left.i == right.i) && (left.f == right.f) && (left.str == right.str);
}
我的预感是,如果 union 体首先包含较大的类型,然后切换到较小的类型,则可能会失败。我看到一些建议提到将 union 包装在一个结构中(如下所示: What is the correct way to check equality between instances of a union? ),该结构跟踪 union 当前的数据类型,但我不知道这实际上是如何实现的。我是否不需要在设置 union 值的每个实例中手动设置 union 类型?
struct myData
{
int dataType;
union {
...
} u;
}
void someFunc()
{
struct myData my_data_value = {0};
my_data_value.u.i = 5;
my_data_value.u.dataType = ENUM_TYPE_INTEGER;
my_data_value.u.f = 5.34;
my_data_value.u.dataType = ENUM_TYPE_FLOAT;
...
}
似乎没有必要将涉及我的 union 的所有代码加倍,只是为了能够对 union 值进行完美比较。我是否错过了一些明显的聪明方法来解决这个问题?
最佳答案
如果您的建议有效,那么您可以使用 memcmp(&left, &right, sizeof left) 实现相同的效果,而无需进行多次比较
。但出于同样的原因,这是行不通的,你的提案也行不通。
首先,分配给一个不占用分配给该 union 的所有字节的 union 成员对未占用的字节有未指定的影响。最有可能的是它们不会从以前的值进行修改,但任何值都是可能的。比较这些字节的值会产生未指定的结果。
您可能认为在分配成员之前将 union 的字节设置为 0 将允许比较工作,但标准不要求未修改未使用的字节。此外,许多编译器会优化清除 union 体的尝试,因为如果下一条语句为 union 体赋予新值,则清除 union 体没有法律效力。
尝试比较非当前值的 union 成员还有其他原因,即使两个值都不包含填充,这些原因也适用。
例如,如果您不知道两个 union 值当前具有相同的事件成员,则可能会得到错误的等价性。 (每个 float
与某些 int
具有相同的位模式,但这两个值肯定不一样。)
不太明显的是,具有不同位模式的两个值实际上可能相等。 (例如,浮点 0.0 和 -0.0 被视为相等。)
最后,并非每个位模式都是有效的浮点
;如果一个或两个 union 值是一个 int
,其位模式对应于一个浮点 NaN,则尝试将这些值与 float
进行比较肯定会产生错误的答案(a NaN 不等于其自身)并且可能引发浮点异常。
简而言之,如果您不知道 union 的事件类型,则无法有效地使用 union 值,除非将其分配给同一 union 类型的另一个对象。这意味着必须有某种机制(内部或外部)来标识 union 体的事件类型。
外部机制(例如,在 yacc 生成的解析器中使用)和内部机制(所谓的“可区分 union ”,正如您在问题末尾所建议的那样)之间的选择将取决于精确的应用程序环境。
关于c - 在c中实现 union 比较的实用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69225419/