C语言中的类型双关和联合

iklwldmw  于 2022-12-11  发布在  其他
关注(0)|答案(2)|浏览(188)

我目前正在做一个项目,构建一个小型的编译器,只是为了好玩。
我已经决定采取构建一个非常简单的虚拟机的方法,这样我就不必担心学习精灵,英特尔组装等的来龙去脉。
我的问题是关于在C中使用联合的类型双关语。我决定在vm的内存中只支持32位整数和32位浮点值。为了便于实现这一点,vm的“主内存”设置如下:

typedef union
{    
    int i;
    float f;
}word;

memory = (word *)malloc(mem_size * sizeof(word));

所以我可以根据指令把内存部分当作int或float。
这是技术上的类型双关语吗?如果我使用int作为内存的单词,然后使用float* 将它们视为float,那肯定是。我目前的方法虽然在语法上有所不同,但我不认为在语义上有所不同。最后,我仍然将内存中的32位视为int或float。
我在网上能得到的唯一信息表明这是依赖于实现的。有没有更便携的方法来实现这一点而不浪费一大堆空间?
我可以这样做,但这样我将占用超过2倍的内存和“重新发明车轮”方面的工会。

typedef struct
{
    int i;
    float f;
    char is_int;
}

编辑

我可能没有把我的问题说清楚。我知道我可以使用一个union中的float或int,而不会有未定义的行为。我所追求的是一种特别的方法,即有一个32位的内存位置,我可以安全地将其用作int或float,而不知道最后一个值集是什么。我想说明使用其他类型的情况。

qhhrdooz

qhhrdooz1#

是的,存储联合体的一个成员并阅读另一个成员是类型双关(假设类型足够不同)。此外,这是唯一一种通用的(任何类型到任何类型)C语言正式支持的类型双关。它在某种意义上是支持的,也就是说,将发生将一种类型的对象作为另一种类型的对象进行读取的物理尝试。这尤其意味着写入联合体的一个成员并阅读另一个成员意味着写入和读取之间存在数据依赖性。然而,仍然需要确保类型双关不会产生陷阱表示。
使用强制转换指针进行文字双关时(通常被理解为“经典”类型双关语),该语言明确声明在一般情况下行为是未定义的(除了将对象的值重新解释为char数组和其他受限情况之外).像愚者这样的编译器实现所谓的“严格别名语义,”这基本上意味着基于指针的类型双关可能不会像您期望的那样工作。(并将)忽略类型双关的读和写之间的数据依赖性,并任意地重新排列它们,从而完全破坏了您的意图。

int i;
float f;

i = 5;
f = *(float *) &i;

可以很容易地重新排列成实际的

f = *(float *) &i;
i = 5;

特别是因为严格别名编译器故意忽略了示例中写操作和读操作之间的数据依赖性。
在现代的C编译器中,当你真的需要将一个对象的值重新解释为另一个类型的值时,你只能通过memcpy-ing将字节从一个对象转换到另一个对象,或者使用基于并集的类型双关。没有其他方法。转换指针不再是一个可行的选择。

xxe27gdn

xxe27gdn2#

只要你只访问最近存储的成员(intfloat),就不会有问题,也没有真实的的实现依赖性。在一个联合成员中存储一个值,然后读取同一个成员是完全安全和定义良好的。
(Note不能保证intfloat的大小相同,尽管我见过的每个系统上都有。)
如果在一个成员中存储一个值,然后读取另一个成员,这就是类型双关。引用最新C11草案中的脚注:
如果用于读取联合对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将被重新解释为新类型中的对象表示,如6.2.6中所述(该过程有时被称为“类型双关”)。这可能是陷阱表示。

相关问题