【问题标题】:Casting from uint16_t to int32_t needs an intermediate cast to int16_t从 uint16_t 转换为 int32_t 需要中间转换为 int16_t
【发布时间】:2020-05-06 19:29:08
【问题描述】:

见我下面的代码。我很困惑为什么我需要先将变量 var_u16 转换为 int16_t 然后再转换为 int32_t 以正确解释为 MyFunc( )。

我原以为var_u16 的最高有效位为 1,当简单地转换为 int32_t 时,编译器会理解这是一个负值。

相反,它仍然被解释为无符号值。为什么需要先将中间转换为 int16_t

我正在使用 gcc 8.3.0

#include <stdio.h>

void MyFunc(int32_t s32Var)
{
    printf("%d\n", s32Var);
}

int main()
{
    uint16_t var_u16 = 0x8000;
    MyFunc((int32_t)var_u16);          // prints 32768  
    MyFunc((int32_t)(int16_t)var_u16); // prints -32768 

    return 0;
}

【问题讨论】:

    标签: c casting type-conversion unsigned signed


    【解决方案1】:

    这里发生的是 正在从一个无符号的 16 位数字转换为一个有符号的 32 位数字。底层表示不相关。

    由于可以存储在uint16_t 中的任何值也可以存储在int32_t 中,因此在转换为新类型时该值不变。因此,值为 32768 的 uint16_t 变为值为 32768 的 int32_t

    随着中间转换为int16_t,情况发生了变化,因为值 32768 无法存储在该类型中,因此该值经历了实现定义的转换,导致int16_t 类型的表达式具有值 -32768。

    【讨论】:

      【解决方案2】:

      在第一种情况下,值不变。这是因为int32_t 可以代表uint16_t 的所有值。当你投射它时,你会保留 0x8000 的值,它只是用零填充。

      编译器会理解这是一个负值

      这是不正确的。 0x8000 是一个完全有效的正数,如果有两种表示形式。

      int16_t 不能保存uint16_t 的所有值,0x8000 是这些值之一。相反,32768 在这种情况下变为 -32768。当您转换为int32_t 时,您将签名扩展int16_t,同时保留-32768

      【讨论】:

      • 好的,现在我明白了。谢谢
      • 您的声明“相反,32768 变为 -32768”。这个我完全不明白。位表示不会仍然完全相同吗?我想我正试图准确地了解引擎盖下发生的事情。那么会有一些标志说这个值现在应该被视为签名吗?
      • 在 16 位的二进制补码表示中,位模式 0x8000 相当于数字 -32768。在无符号表示中,位模式0x8000 等价于 32768。您要求int16_t 保存数字 32768,这是它完全无法做到的。
      • 我知道,但就编译器下面发生的情况而言。它怎么知道当我们转换为 int16_t 时,它现在应该将此值视为有符号值,即使位模式仍然相同?
      • 编译器生成以特定方式处理的代码。没有任何东西将任何变量标记为特定类型。根据您提供的代码,编译器确切地知道一切是什么类型。如果它知道它已签名,那么它会生成将其用作已签名的指令。如果它知道它是 16 位的,它会生成将其用作 16 位的指令。
      【解决方案3】:

      int32_t 类型的对象可以存储uint16_t 类型的值。

      来自 C 标准(6.2.6.2 整数类型)

      5 未指定任何填充位的值。54) 有效 (非陷阱)有符号整数类型的对象表示,其中 符号位为零是对应的有效对象表示 无符号类型,并且应该代表相同的值

      当一个带符号整数类型的对象(例如int16_t)被转换为更大的整数类型(例如int32_t)时,如果该对象具有负值,则其符号位被传播到更大的整数类型。

      例如,如果您有以下声明

      signed char c = -1;
      

      那么它在两个的恭维系统中具有内部表示,例如

      11111111
      

      如果你把它转换成 int16_t 或 uint16_t 然后喜欢

      int16_t i = c;
      uint17_t ui = c;
      

      那么在这两种情况下,内部表示都会看起来很像

      11111111 11111111
      

      但是,如果您有一个无符号字符类型的对象,例如

      unsigned char c = 255;
      

      具有类似的内部表示

      11111111
      

      那么在这种情况下,变量 i 和 ui 声明如下

      int16_t i = c;
      uint17_t ui = c;
      

      将具有以下内部表示

      00000000 11111111
      

      因为值255 是这些类型的对象的有效值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-07
        相关资源
        最近更新 更多