【问题标题】:How do C/C++ compilers handle type casting between types with different value ranges?C/C++ 编译器如何处理具有不同值范围的类型之间的类型转换?
【发布时间】:2018-03-08 22:49:21
【问题描述】:

如何在不丢失编译器内部数据的情况下进行类型转换?

例如:

 int i = 10;
 UINT k = (UINT) k;

 float fl = 10.123;
 UINT  ufl = (UINT) fl; // data loss here?

 char *p = "Stackoverflow Rocks";
 unsigned char *up = (unsigned char *) p;

编译器如何处理这种类型的类型转换?非常感谢显示位的低级示例。

【问题讨论】:

    标签: c++ c types casting compiler-construction


    【解决方案1】:

    首先请注意,强制转换是将一种类型的值转换为另一种类型的值的显式请求。强制转换也总是会产生一个新对象,它是强制转换运算符返回的临时对象。但是,转换为引用类型不会创建新对象。该值引用的对象被重新解释为不同类型的引用。

    现在回答你的问题。请注意,有两种主要的转化类型:

    • 促销:可以认为这种类型从可能更窄的类型转换为更宽的类型。从 char 到 int、short 到 int、float 到 double 的转换都是促销。
    • 转换:这些允许从 long 转换为 int,从 int 转换为 unsigned int 等等。它们原则上会导致信息丢失。例如,如果将 -1 分配给未签名的类型对象,会发生什么情况是有规则的。在某些情况下,错误的转换可能会导致未定义的行为。如果您分配的 double 大于 float 可以存储的 float,则未定义行为。

    让我们看看你的演员阵容:

    int i = 10; 
    unsigned int k = (unsigned int) i; // :1
    
    float fl = 10.123;
    unsigned int  ufl = (unsigned int) fl; // :2
    
    char *p = "Stackoverflow Rocks"; 
    unsigned char *up = (unsigned char *) p; // :3
    
    1. 此转换导致发生转换。不会发生数据丢失,因为unsigned int 保证存储 10。如果整数为负数,则该值基本上会环绕 unsigned int 的最大值(参见 4.7/2)。
    2. 10.123 的值被截断为 10。显然,它确实导致信息丢失。由于 10 适合无符号整数,因此定义了行为。
    3. 这实际上需要更多的关注。首先,不推荐将字符串文字转换为char*。但是,让我们在这里忽略它。 (见here)。更重要的是,如果转换为无符号类型会发生什么?实际上,根据 5.2.10/7 未指定其结果(请注意,在这种情况下,该转换的语义与使用 reinterpret_cast 相同,因为这是唯一能够做到这一点的 C++ 转换):

    指向对象的指针可以显式转换为指向对象的指针 不同类型的对象。除了将“指向 T1 的指针”类型的右值转换为“指向 T2 的指针”类型(其中 T1 和 T2 是对象类型,并且 T2 的对齐要求不比 T1 的对齐要求更严格)并返回其原始类型会产生原始指针值,这种指针转换的结果是未指定的。

    因此,只有在再次转换回 char * 后才能安全使用指针。

    【讨论】:

    • 注意:这仅适用于 C++ 而不是 C。
    【解决方案2】:

    您的示例中的两个 C 样式转换是不同类型的转换。在 C++ 中,您通常会编写它们

    unsigned int uf1 = static_cast<unsigned int>(fl);
    

    和

    unsigned char* up = reinterpret_cast<unsigned char*>(p);
    

    第一个执行算术转换,截断浮点数,因此存在数据丢失。

    第二个不改变数据——它只是指示编译器将指针视为不同的类型。这种演员需要小心:它可能非常危险。

    【讨论】:

      【解决方案3】:

      C 和 C++ 中的“类型”是在编译器中处理变量时分配给变量的属性。该属性在运行时不再存在,C++ 中的虚函数/RTTI 除外。

      编译器使用变量的类型来确定很多事情。例如,在将 float 分配给 int 时,它会知道它需要转换。这两种类型可能都是 32 位,但含义不同。 CPU 很可能有一条指令,否则编译器会知道调用转换函数。 IE。 &amp; __stack[4] = float_to_int_bits(&amp; __stack[0])

      从 char* 到 unsigned char* 的转换更加简单。那只是一个不同的标签。在位级别,p 和 up 是相同的。编译器只需要记住 *p 需要符号扩展,而 *up 不需要。

      【讨论】:

        【解决方案4】:

        演员表的含义不同,具体取决于它们的含义。它们可以只是数据类型的重命名,所表示的位没有变化(整数类型和指针之间的大多数转换都是这样的),或者甚至不保留长度的转换(例如大多数编译器上的 double 和 int 之间) .在许多情况下,强制转换的含义只是未指定,这意味着编译器必须做一些合理的事情,但不必准确记录。

        强制转换甚至不需要产生可用值。类似 char * cp; float * fp; cp = malloc(100); fp = (float *)(cp + 1); 几乎肯定会导致指向浮动的指针未对齐,如果程序尝试使用它,这将在某些系统上使程序崩溃。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-11-21
          • 2012-11-02
          • 2021-12-06
          • 2021-08-12
          • 2020-03-10
          • 1970-01-01
          相关资源
          最近更新 更多