给定一个类型为"uint16_t"的变量,(int16_t)uVal和*(int16_t*)&uVal之间有什么区别吗?
Given a variable whose type is `uint16_t`, is there any difference between (int16_t) uVal and *(int16_t*)&uVal?
它们似乎是等价的。但我不知道为什么。 以下是相关的代码片段:
#include<iostream>
void foo(uint16_t uVal)
{
int16_t auxVal1 = (int16_t) uVal;
int16_t auxVal2 = *(int16_t*)&uVal;
std::cout << auxVal1<< std::endl;
std::cout << auxVal2<< std::endl;
std::cout << (uint16_t)auxVal1 << std::endl;
std::cout << *(uint16_t*)&auxVal2 << std::endl;
}
int main()
{
foo(0xFFFF);
std::cout << std::endl;
foo(1);
}
这是输出:
-1
-1
65535
65535
1
1
1
1
(int16_t)uval
将uint16_t
值转换为int16_t
值。对于1
,这按预期工作,对于0xffff
而言,它是实现定义的行为,因为0xffff
不适合int16_t
的范围。(https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_conversions)。自 C++20 起,它被定义为产生预期值(见下文)。
*(int16_t*)&uval
首先将uint16_t*
指针强制转换为int16_t*
指针,然后取消引用它。使用 C 样式指针强制转换时,表达式等效于*reinterpret_cast<int16_t*>(&uval)
。(https://en.cppreference.com/w/cpp/language/explicit_cast)。static_cast
是不可能的,因为uint16_t
和int16_t
是不同的类型。 因为它们也不是"类似类型",所以取消引用生成的int16_t*
指针也是未定义的行为 (https://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing)。
因为它是未定义的行为,所以表达式理论上可以产生任何东西,但是对于普通编译器(没有优化),第一个表达式会尝试将uint16_t
转换为int16_t
,而对于第二个表达式,它会尝试访问原始uint16_t
值,就好像它是int16_t
一样, 无需修改它。
这会产生相同的值,因为有符号整数值存储在二进制补码中的方式:正值在有符号和无符号类型中具有相同的按位表达式。0x0001
对两者都意味着1
。但是0xffff
(全一个字节)对于uint16_t
意味着65535
,对于int16_t
意味着-1
。
因此,(int16_t)uval
(以及(uint16_t)sval
)根本不需要修改按位值,因为int16_t
和uint16_t
范围内的所有值对于两者的表示方式相同。对于范围之外的值,这是未定义的行为,因此在这种情况下,编译器也不会修改按位值。
获得第二个表达式的效果(访问原始按位数据,就好像它是另一种类型)而不定义行为的唯一方法是使用memcpy
:int16_t sval; std::memcpy(&sval, &uval, 2);
。