在C中输入punning和Unions

我正在开发一个项目来构建一个小编译器,只是为了它。

我已经决定采用构建一个非常简单的虚拟机来实现目标,所以我不必担心学习精灵,英特尔组装等的来龙去脉。

我的问题是使用工会在C中打字。 我决定只在vm的内存中支持32位整数和32位浮点值。 为方便起见,vm的“主内存”设置如下:

typedef union { int i; float f; }word; memory = (word *)malloc(mem_size * sizeof(word)); 

所以我可以根据指令将内存部分视为int或float。

这在技术上是否类型? 当然,如果我使用整数作为记忆的话,然后使用浮点*来像浮点数一样对待它们。 我目前的方法虽然在语法上有所不同,但我认为在语义上不同。 最后,我仍然将内存中的32位视为int或float。

我在网上提出的唯一信息表明这是依赖于实现的。 是否有更便携的方式来实现这一点而不浪费一大堆空间?

我可以做到以下几点,但接下来我会占用相当于工会的2倍以上的记忆和“重新发明轮子”。

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

编辑

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

是的,存储一个联合成员并读取另一个成员是类型双关语(假设类型完全不同)。 而且,这是C语言正式支持的唯一一种通用(任何类型到任何类型)类型的惩罚。 在某种意义上,语言支持在这种情况下承诺将实际发生类型双关语,即,将发生将一种类型的对象作为另一种类型的对象进行读取的物理尝试。 除此之外,它意味着编写联合的一个成员并读取另一个成员意味着写入和读取之间的数据依赖性。 但是,这仍然会让您承担确保类型惩罚不会产生陷阱表示的负担。

当你使用用于类型惩罚的铸造指针(通常被理解为“经典”类型双关语)时,语言明确指出一般情况下行为是未定义的(除了将对象的值重新解释为char和其他受限情况的数组) 。 像GCC这样的编译器实现了所谓的“严格别名语义”,这基本上意味着基于指针的类型双关语可能无法正常工作。 例如,编译器可能(并且将)忽略类型处罚读取和写入之间的数据依赖性,并任意重新排列它们,从而完全破坏您的意图。 这个

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

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

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

特别是因为严格别名的编译器故意忽略了示例中写入和读取之间数据依赖性的可能性。

在现代C编译器中,当您确实需要对一个对象值进行物理重新解释作为另一个类型的值时,您将被限制为从一个对象到另一个对象的memcpy -ing字节或基于联合的类型双关语。 没有别的办法。 投射指针不再是可行的选择。

只要您只访问最近存储的成员( intfloat ),就没有问题,也没有真正的实现依赖性。 将值存储在union成员中然后读取同一个成员是非常安全且定义良好的。

(注意,不能保证intfloat的大小相同,尽管它们出现在我看过的每个系统上。)

如果您将值存储在一个成员中,然后读取另一个成员,那就是类型惩罚。 在最新的C11草案中引用脚注:

如果用于读取union对象内容的成员与上次用于在对象中存储值的成员不同,则将值的对象表示的相应部分重新解释为新类型中的对象表示forms在6.2.6中描述(一个过程有时称为“类型双关语”)。 这可能是陷阱表示。