以下代码在C ++中合法吗?
int get_i(int idx) { ... }
float transform(int i) { ... }
void use(float f) { ... }
static_assert(sizeof(int) == sizeof(float));
void* buffer = std::malloc(n * sizeof(int));
int* i_buffer = reinterpret_cast<int*>(buffer);
float* f_buffer = reinterpret_cast<float*>(buffer);
// Fill int values into the buffer
for(int idx = 0; idx < n; ++idx)
i_buffer[idx] = get_i(idx);
// Transform int value to float value, and overwrite
// (maybe violates strict aliassing rule?)
for(int idx = 0; idx < n; ++idx)
f_buffer[idx] = transform(i_buffer[idx]);
for(int idx = 0; idx < n; ++idx)
use(f_buffer[idx]);
第二步将缓冲区值读取为int
,然后在其位置写入float
。此后它再也不会通过i_buffer
访问内存,因此在读取时没有类型别名。
然而,分配f_buffer[idx] =
将float
对象写入UB的int
对象中。
有没有一种方法可以使编译器认为这意味着int
的生存期应该结束,并且应该在其位置构造一个float
对象,以便没有类型混叠。 ?
答案 0 :(得分:2)
然而,赋值
f_buffer[idx] =
将一个float对象写入一个int对象即UB。
是的,上面的代码中断了type aliasing rules。
要解决此问题,可以使用并集来获取您的值:
union U {
float f;
int i;
};
然后访问联合的相应成员。
当您这样做时:
buffer[idx].i = ...; // make i the active union member
...
buffer[idx].f = transform(buffer[idx].i); // make f the active union member
它避免了UB,因为buffer[idx].i
的生存期结束了,buffer[idx].f
的生存期开始了。