【发布时间】:2011-11-26 09:36:27
【问题描述】:
我有以下代码:
short myShort = 23948;
byte myByte = (byte)myShort;
现在我没想到myByte 包含值 23948。我猜它会包含 255(我相信一个字节的最大值)。
但是,它包含 140,这让我想知道为什么;幕后究竟发生了什么?
请注意,我不是在找人来解决 23948 无法放入一个字节的问题,我只是想知道底层的实现
【问题讨论】:
我有以下代码:
short myShort = 23948;
byte myByte = (byte)myShort;
现在我没想到myByte 包含值 23948。我猜它会包含 255(我相信一个字节的最大值)。
但是,它包含 140,这让我想知道为什么;幕后究竟发生了什么?
请注意,我不是在找人来解决 23948 无法放入一个字节的问题,我只是想知道底层的实现
【问题讨论】:
Short 是一个 2 字节类型,而一个字节是一个单字节。当您从两个字节转换为一个字节时,您会强制系统进行调整,并且原始字节之一(最重要的字节)被丢弃并且数据丢失。 23948(二进制:0101 1101 1000 1100)的值剩下的是 140,二进制转换为 1000 1100。所以你要从:
0101 1101 1000 1100 (2 byte decimal value 23948)
到:
1000 1100 (1 byte decimal value 140)
您只能通过显式强制转换来做到这一点。如果您尝试在不进行强制转换的情况下将短字节分配给一个字节,编译器会抛出一个错误,因为可能会丢失数据:
无法将类型“short”隐式转换为“byte”。显式 存在转换(您是否缺少演员表?)
另一方面,如果您将字节转换为短字节,则可以隐式执行,因为不会丢失任何数据。
using System;
public class MyClass
{
public static void Main()
{
short myShort = 23948;
byte myByte = (byte)myShort; // ok
myByte = myShort; // error:
Console.WriteLine("Short: " + myShort);
Console.WriteLine("Byte: " + myByte);
myShort = myByte; // ok
Console.WriteLine("Short: " + myShort);
}
}
算术溢出和未经检查的上下文:
using System;
public class MyClass {
public static void Main() {
unchecked {
short myShort = 23948;
byte myByte = (byte)myShort; // ok
myByte = myShort; // still an error
int x = 2147483647 * 2; // ok since unchecked
}
}
}
【讨论】:
checked 还是unchecked,这取决于代码,还取决于编译器设置(或VS 中的“检查算术上溢/下溢”复选框)
unchecked 是默认值;现在改成checked 看看有什么不同
checked 还是unchedked,代码myByte = myShort; // still an error 的铸造部分的结果都是相同的。编译器错误总是一样的:Cannot implicitly convert type 'short' to 'byte'. An explicit conversion exists (are you missing a cast?)
byte myByte = (byte)myShort;
基本上它只需要最后 8 位......但一般来说,当您发现一些让您感到惊讶的行为时,下一步应该是查阅规范。从第 6.2.1 节开始,我特别强调了与这种情况相关的情况。
对于从一个整数类型到另一个整数类型的转换,处理取决于发生转换的溢出检查上下文(第 7.6.12 节):
- 在检查的上下文中,如果源操作数的值在目标类型的范围内,则转换成功,但如果源操作数的值超出目标类型的范围,则抛出 System.OverflowException。
- 在未经检查的上下文中,转换始终成功,并按如下方式进行。
- 如果源类型大于目标类型,则源值通过丢弃其“额外”最高有效位来截断。然后将结果视为目标类型的值。
- 如果源类型小于目标类型,则源值进行符号扩展或零扩展,使其大小与目标类型相同。如果源类型已签名,则使用符号扩展;如果源类型是无符号的,则使用零扩展。然后将结果视为目标类型的值。
- 如果源类型与目标类型的大小相同,则源值被视为目标类型的值。
【讨论】:
视情况而定;在checked 上下文中,你会得到一个很大的异常;在unchecked 上下文(默认)中,您可以保留最后一个字节的数据,就像您这样做一样:
byte b = (byte)(value & 255);
【讨论】:
在您的特定情况下,当您查看值的位时,行为非常简单:
short myShort = 0x5D8C; // 23948
byte myByte = (byte)myShort; // myShort & 0xFF
Console.WriteLine("0x{0:X}", myByte); // 0x8C or 140
【讨论】:
只保留最后 8 位。二进制的 23948 是 101110110001100b。最后8位是10001100b,等于140。
【讨论】:
当您将整数类型转换为“较小”的整数类型时,仅考虑较小的权重位。从数学上讲,就好像您使用了模运算一样。所以你得到值 140 因为 23948 模 256 是 140。
将 long 转换为 int 将使用相同的机制。
【讨论】:
short 有 65536 个可能的值:32767 个正数、32768 个负数和 0。因此,使用 short 的每个未经检查的计算都按照模 65536 进行操作。执行强制转换时,运行时只考虑较小的位(正如公认的答案解释得很好)。从数学上讲,这与找到范围内的全等值相同。在这种情况下,-30536,因为 35000 太大了。 (注意 -30536 + 65536 = 35000)。
这样做的结果是一样的:
byte myByte = (byte)(myShort & 0xFF);
8 位以上的所有内容都被简单地丢弃。 23948(0x5D8C)的低八位是140(0x8C)。
【讨论】:
嗯...因为当你将 short(2 个字节)转换为 byte(1 个字节)时,它只得到第一个字节,而 23948 的第一个字节代表 140。
【讨论】:
23948 % 256 = 140,转换后丢失了最高有效字节,所以输出为140
【讨论】:
就像当你有一个两位数“97”,然后将其转换为一位数时,你失去了 9,只保留了“7”
【讨论】: