c - 在c中实现 union 比较的实用方法

标签 c unions ansi-c

出于某些测试目的,我需要比较两个 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/

相关文章:

c++ - 对库对象的 C 引用

c - 操作系统架构: Kernel and Standard Library interoperability

c++ - 应该将 float 转换为字节数组的程序中的运行时错误

c - 使用 union 变量分配两个值

c - 错误 : initializer element is not computable at load time

while(1)之前的代码不运行

c - 处理文件或标准输入

C - 在函数中分配矩阵

c++ - 如何在使用 gdb 调试期间从 stdin 读取?

c - 使用 union 强制 C 位域对齐