将double(不是指针)转换为void指针

act*_*ual 0 c pointers casting

这种方式是合法的吗?

void probability(void **value)
{
    double v = 0.1234;
    *value = (void *) *(uint64_t *) &v;
}
Run Code Online (Sandbox Code Playgroud)

我知道,这是一件坏事,但我100%肯定sizeof(double)= sizeof(void *)= sizeof(uint64_t)在目标机器上.

Ste*_*sop 6

这是未定义的行为,因为您的代码违反了严格的别名规则.

允许编译器假设指向大多数不相关类型的指针不指向相同的内存.你创建一个uint64_t *(转换的结果)指向内存,它实际上是一个double,你希望从该指针读取给你一个与double有关的值.

严格别名规则的目的是允许编译器进行各种优化以破坏此代码 - 最有可能的是编译器可以"推断" v未使用的并且从不初始化它,因为它永远不会通过任何有效名称访问或指针,只有无效的别名.

我没有检查过这个特定的代码,但是GCC实际上确实依赖于高优化时的严格别名,并且会破坏这种代码.

解决严格别名问题的方法是memcpy:

assert(sizeof(*value) == sizeof(v));
memcpy(value, &v, sizeof(*value));
Run Code Online (Sandbox Code Playgroud)

一旦完成,或者如果您使用的编译器不依赖严格别名,或者可以避免依赖(--no-strict-aliasing),那么您仍然有问题.该标准实际上并不保证与地址大小相同的每个数值实际上都可以表示void*.例如,实现(实际上)指针中有填充位是合法的,如果尝试在填充位中创建错误值的指针值,则会崩溃.实际上,这不会发生在你称之为"正常"的任何硬件上,但是标准不允许你的代码.