0

以这种方式投射是否合法?

void probability(void **value)
{
    double v = 0.1234;
    *value = (void *) *(uint64_t *) &v;
}

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

4

3 回答 3

6

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

允许编译器假设指向大多数不相关类型的指针不指向相同的内存。您创建了一个uint64_t *(强制转换的结果),它指向“实际上”是双精度的内存,并且您希望从该指针中读取数据,从而为您提供一个与双精度有关的值。

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

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

解决严格混叠问题的方法是memcpy

assert(sizeof(*value) == sizeof(v));
memcpy(value, &v, sizeof(*value));

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

于 2012-04-23T13:28:01.223 回答
3

严格来说,没有。那个部分

*(uint64_t*)&v;

是未定义的行为。uint64_tto的转换void*是合法的,但可能没有意义,这种转换是实现定义的(6.3.2.3)。

于 2012-04-23T13:23:05.303 回答
1

未定义的行为。不管你有多确定。

于 2012-04-23T13:23:01.123 回答