这样转换合法吗?
void probability(void **value)
{
double v = 0.1234;
*value = (void *) *(uint64_t *) &v;
}
我知道,这是一件坏事,但我 100% 确定 sizeof(double)
= sizeof(void *)
= sizeof( uint64_t)
在目标机器上。
最佳答案
这是未定义的行为,因为您的代码违反了严格的别名规则。
允许编译器假设指向大多数不相关类型的指针不指向同一内存。您创建了一个指向内存的 uint64_t *
(转换的结果),它“实际上”是一个 double ,并且您期望从该指针读取一个值,该值与双。
严格别名规则的目的是允许编译器进行各种优化来破坏此代码 - 最有可能的是编译器可以“推断” v
未使用并且永远不会初始化它,因为它永远不会通过任何有效名称或指针访问,只能通过无效别名访问。
我没有检查过这个特定的代码,但 GCC 实际上在高度优化时确实依赖于严格的别名,并且会破坏这种代码。
解决严格别名问题的方法是使用memcpy
:
assert(sizeof(*value) == sizeof(v));
memcpy(value, &v, sizeof(*value));
完成此操作后,或者如果您使用的编译器不依赖于严格的别名,或者可以避免这种依赖(--no-strict-aliasing
),那你还是有问题。该标准实际上并不保证每个与地址大小相同的数值实际上都可以在 void*
中表示。例如,实现(实际上)在指针中有填充位是合法的,如果您尝试创建一个填充位中有错误值的指针值,则会崩溃。实际上,这不会发生在您称之为“正常”的任何硬件上,但是标准不允许您的代码。
关于将 double(不是指针)转换为 void 指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10281329/