C/C++ 编译器如何处理具有不同值范围的类型之间的类型转换?
如何在不丢失编译器内部数据的情况下进行类型转换?
例如:
int i = 10;UINT k = (UINT) k;浮动FL = 10.123;UINT ufl = (UINT) fl;//这里数据丢失?char *p = "Stackoverflow Rocks";unsigned char *up = (unsigned char *) p;
编译器如何处理这种类型转换?一个显示这些位的低级示例将不胜感激.
解决方案好吧,首先请注意,强制转换是将一种类型的值转换为另一种类型的值的显式请求.强制转换也总是会产生一个新对象,它是由强制转换操作符返回的临时对象.但是,转换为引用类型不会创建新对象.值引用的对象被重新解释为不同类型的引用.
现在回答你的问题.请注意,有两种主要类型的转化:
- 促销:这种类型可以被认为是从可能更窄的类型转换为更宽的类型.从 char 转换为 int,short 转换为 int,float 转换为 double 都是促销.
- 转换:这些允许从 long 转换为 int,int 转换为 unsigned int 等等.它们原则上会导致信息丢失.例如,如果您将
-1
分配给无符号类型的对象会发生什么,则有一些规则.在某些情况下,错误的转换可能会导致未定义的行为.如果您将一个大于浮点数可以存储的双精度数分配给浮点数,则行为未定义.
让我们看看你的演员表:
int i = 10;无符号整数 k = (无符号整数) i;//:1浮动FL = 10.123;unsigned int ufl = (unsigned int) fl;//:2char *p = "Stackoverflow Rocks";unsigned char *up = (unsigned char *) p;//:3
- 此转换会导致转换发生.不会发生数据丢失,因为 10 保证由
unsigned int
存储.如果整数为负数,则该值基本上会环绕无符号整数的最大值(请参阅4.7/2). - 值
10.123
被截断为 10.很明显,它确实会导致信息丢失.由于 10 适合 unsigned int,因此定义了行为. - 这实际上需要更多关注.首先,不推荐使用从字符串文字到
char*
的转换.但让我们在这里忽略它.(请参阅此处).更重要的是,如果转换为无符号类型会发生什么?实际上,根据 5.2.10/7 未指定结果(请注意,该转换的语义与在这种情况下使用 reinterpret_cast 相同,因为这是唯一能够执行此操作的 C++ 转换):
<块引用>
指向对象的指针可以显式转换为指向对象的指针不同类型的对象.除了将指向 T1 的指针"类型的右值转换为指向 T2 的指针"类型(其中 T1 和 T2 是对象类型并且 T2 的对齐要求不比 T1 的对齐要求更严格)并返回其原始类型之外,会产生原始指针值,这种指针转换的结果是未指定的.
因此,只有在再次转换回 char *
后才能安全地使用指针.
How do type casting happen without loss of data inside the compiler?
For example:
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;
How does the compiler handle this type of typecasting? A low-level example showing the bits would be highly appreciated.
解决方案Well, first note that a cast is an explicit request to convert a value of one type to a value of another type. A cast will also always produce a new object, which is a temporary returned by the cast operator. Casting to a reference type, however, will not create a new object. The object referenced by the value is reinterpreted as a reference of a different type.
Now to your question. Note that there are two major types of conversions:
- Promotions: This type can be thought of casting from a possibly more narrow type to a wider type. Casting from char to int, short to int, float to double are all promotions.
- Conversions: These allow casting from long to int, int to unsigned int and so forth. They can in principle cause loss of information. There are rules for what happens if you assign a
-1
to an unsigned typed object for example. In some cases, a wrong conversion can result in undefined behavior. If you assign a double larger than what a float can store to a float, the behavior is not defined.
Let's look at your casts:
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
- This cast causes a conversion to happen. No loss of data happens, since 10 is guaranteed to be stored by an
unsigned int
. If the integer were negative, the value would basically wrap around the maximal value of an unsigned int (see 4.7/2). - The value
10.123
is truncated to 10. Here, it does cause lost of information, obviously. As 10 fits into an unsigned int, the behavior is defined. - This actually requires more attention. First, there is a deprecated conversion from a string literal to
char*
. But let's ignore that here. (see here). More importantly, what does happen if you cast to an unsigned type? Actually, the result of that is unspecified per 5.2.10/7 (note the semantics of that cast is the same as using reinterpret_cast in this case, since that is the only C++ cast being able to do that):
A pointer to an object can be explicitly converted to a pointer to an object of different type. Except that converting an rvalue of type "pointer to T1" to the type "pointer to T2" (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.
So you are only safe to use the pointer after you cast back to char *
again.
相关文章