【问题标题】: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
- 此转换导致发生转换。不会发生数据丢失,因为
unsigned int 保证存储 10。如果整数为负数,则该值基本上会环绕 unsigned int 的最大值(参见 4.7/2)。
-
10.123 的值被截断为 10。显然,它确实导致信息丢失。由于 10 适合无符号整数,因此定义了行为。
- 这实际上需要更多的关注。首先,不推荐将字符串文字转换为
char*。但是,让我们在这里忽略它。 (见here)。更重要的是,如果转换为无符号类型会发生什么?实际上,根据 5.2.10/7 未指定其结果(请注意,在这种情况下,该转换的语义与使用 reinterpret_cast 相同,因为这是唯一能够做到这一点的 C++ 转换):
指向对象的指针可以显式转换为指向对象的指针
不同类型的对象。除了将“指向 T1 的指针”类型的右值转换为“指向 T2 的指针”类型(其中 T1 和 T2 是对象类型,并且 T2 的对齐要求不比 T1 的对齐要求更严格)并返回其原始类型会产生原始指针值,这种指针转换的结果是未指定的。
因此,只有在再次转换回 char * 后才能安全使用指针。
【解决方案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。
& __stack[4] = float_to_int_bits(& __stack[0])
从 char* 到 unsigned char* 的转换更加简单。那只是一个不同的标签。在位级别,p 和 up 是相同的。编译器只需要记住 *p 需要符号扩展,而 *up 不需要。
【解决方案4】:
演员表的含义不同,具体取决于它们的含义。它们可以只是数据类型的重命名,所表示的位没有变化(整数类型和指针之间的大多数转换都是这样的),或者甚至不保留长度的转换(例如大多数编译器上的 double 和 int 之间) .在许多情况下,强制转换的含义只是未指定,这意味着编译器必须做一些合理的事情,但不必准确记录。
强制转换甚至不需要产生可用值。类似
char * cp;
float * fp;
cp = malloc(100);
fp = (float *)(cp + 1);
几乎肯定会导致指向浮动的指针未对齐,如果程序尝试使用它,这将在某些系统上使程序崩溃。