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