这是严格违反别名的classical example:
std::uint32_t foo(float* f, std::uint32_t* i) {
*i = 1;
*f = 2;
return *i;
}
int main() {
std::uint32_t i = 3;
foo(reinterpret_cast<float*>(&i), &i);
}
但是假设我们添加第二个reinterpret_cast
:
static_assert(alignof(float) == alignof(std::uint32_t));
std::uint32_t foo(float* f, std::uint32_t* i) {
*i = 1;
*reinterpret_cast<std::uint32_t*>(f) = 2;
return *i;
}
int main() {
std::uint32_t i = 3;
std::uint32_t j = foo(reinterpret_cast<float*>(&i), &i);
assert(j == 2);
}
此代码正确(不调用未定义的行为)吗?
标准[expr.reinterpret.cast] reads:
注意:将类型为“指向
T1
的指针”的prvalue转换为“指向“T2
的指针”的类型(其中T1
和T2
是对象类型,其中T2
的对齐要求不严格于T1
的对齐要求,并返回其原始类型将产生原始指针值。
我们使用类型为std::uint32_t*
的原始指针值来访问类型为std::uint32_t
的左值。
启用优化功能后,GCC和Clang都会生成正确的汇编代码:
foo(float*, unsigned int*):
mov dword ptr [rsi], 1
mov dword ptr [rdi], 2
mov eax, dword ptr [rsi]
ret
答案 0 :(得分:1)
以下是相应的规范性文本expr.static_cast/13:
“指针指向cv1无效”类型的prvalue可以转换为“指针指向cv2 T的类型”的prvalue,其中T是对象类型,并且cv2是与cv-qualification相同或大于cv-qualification的对象。 ,cv1。如果原始指针值表示内存中一个字节的地址A,而A不满足T的对齐要求,则未指定结果指针值。否则,如果原始指针值指向对象a,并且存在类型T(忽略cv限定)的对象b,该对象可与a进行指针互换,则结果是指向b的指针。否则,转换后指针值不变。
(此文本与之相关,因为在这种情况下,reinterpret_cast<T*>
的结果为static_cast<T *>(static_cast<void *>(...))
)
因此,如果满足对齐要求,则所有转换(uint32_t
-> float
或float
-> uint32_t
)都不会更改指针值。然后您访问该对象作为其类型。这里没有UB。