当我想将整数(例如32位整数/ int)转换为另一个整数类型(例如8位整数/字节)时,是否存在差异。以下是我可以转换它的两种方式的示例代码:
byte foo(int value)
{
//return value; <-- this causes problems because I need to convert it to byte
//First way(most people use this):
return (byte)value; //this involves casting the value and also works if value is floating point type
//Second way:
return value & byte.MaxValue; //byte.MaxValue is a constant that is 255
}
两者之间有什么区别吗?我知道按位操作仅适用于整数类型。我知道第二种方式不太可读或推荐。除此之外,两种方式的输出都有差异。这不仅适用于int和byte,而且适用于每个整数 - 整数类型组合。
好的,似乎这个操作在不同的语言中有不同的行为。我不想看到差异,所以请发布C ++ / C#/ D的答案。
我也忘记了我的意思是无符号整数(没有签名)。所以它适用于所有无符号整数类型。
答案 0 :(得分:3)
在C#中,如果int超出checked
上下文中的和范围,则将int转换为字节将引发异常。否则,强制转换就像C ++一样。
类型提升在C#中工作,就像在C ++中一样(如Mark B所述)。
为了比较,请查看这三种方法生成的IL:
byte foo1(uint value)
{
return (byte) value;
}
.method private hidebysig instance uint8 foo1(int32 'value') cil managed
{
.maxstack 8
L_0000: ldarg.1
L_0001: conv.u1
L_0002: ret
}
Versus
byte foo2(uint value)
{
checked
{
return (byte)value;
}
}
.method private hidebysig instance uint8 foo2(uint32 'value') cil managed
{
.maxstack 8
L_0000: ldarg.1
L_0001: conv.ovf.u1.un
L_0002: ret
}
对于ANDing:
byte foo3(int value)
{
return (byte)(value & byte.MaxValue);
}
.method private hidebysig instance uint8 foo3(uint32 'value') cil managed
{
.maxstack 8
L_0000: ldarg.1
L_0001: ldc.i4 255
L_0006: and
L_0007: conv.u1
L_0008: ret
}
这再次使用conv.u1
,就像第一种方法一样,所以它所做的只是引入了conv.u1
指令忽略的额外位的开销。
因此,在C#中,如果您不关心范围检查,我会使用转换。
有趣的是,在C#中,这会给你一个编译错误:
Trace.Assert(((byte)256) == 0); // Compiler knows 256 is out of range.
这不会产生编译错误:
int value = 256;
Trace.Assert(((byte)value) == 0); // Compiler doesn't care.
当然,这也不会产生编译错误:
unchecked
{
Trace.Assert(((byte)256) == 0);
}
奇怪的是,第一个给出编译器错误,即使默认情况下它在运行时未经检查。我猜默认会检查编译时间!
答案 1 :(得分:0)
在C ++中,获得结果的方式完全不同,因为&
的操作数将被提升为两种类型中较大者的大小。如果您&
的“最大”值恰好是签名的,那么您将签署扩展,并且按位操作将不太可能具有所需的效果。
我个人更喜欢明确的return static_cast<char>(value);