给定一个类型为"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?

本文关键字:int16 uVal 区别 什么 之间 一个 类型 变量 uint16      更新时间:2024-09-21

它们似乎是等价的。但我不知道为什么。 以下是相关的代码片段:

#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_tint16_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_tuint16_t范围内的所有值对于两者的表示方式相同。对于范围之外的值,这是未定义的行为,因此在这种情况下,编译器也不会修改按位值。


获得第二个表达式的效果(访问原始按位数据,就好像它是另一种类型)而不定义行为的唯一方法是使用memcpyint16_t sval; std::memcpy(&sval, &uval, 2);