我需要在有符号整数和它们的内部表示之间转换为一系列字节。在C中我使用的函数如下:
unsigned char hibyte(unsigned short i)
{return i>>8;}
unsigned char lobyte(unsigned short i)
{return i & 0xFF;}
unsigned short makeshort(unsigned char hb, unsigned char lb)
{return ((short)hb << 8) | (short)lb;}
问题是这个代码在C#下不起作用,因为签名/无符号强制转换的规则是不一样的:因为我理解C#强制转换的意思是转换值,而在C代码中,有符号/无符号类型之间的转换不会修改基础数据。此外,在C#中,对于签名号码,&gt;&gt;操作员在符号位移位。所有这些使我很难将我的代码转换为C#例如。
1)C#功能
public static byte hibyte(short i)
{return (byte) (i>>8);}
如果我是否定的话,会抛出溢出异常
2)C#功能
public static ushort makeshort(byte hb, byte lb)
{return (short) (((ushort)hb << 8) | (ushort)lb); }
如果结果为short,则抛出溢出异常。这里 表达式“(ushort)hb&lt;&lt; 8”起作用,因为移位是在无符号数上完成的。但后来我需要将相同的数据解释为有符号整数,我不知道该怎么做。我理解C#这样的类C演员是作弊的,因为正值可能会变成负值,但这是我实际需要的(例如,处理从设备读取的字节流等)。目前我正在使用对于像这样的所有二进制操作编译为非托管dll的C代码,但这不是很优雅,我确信这可以在C#中以某种方式(可能简单地)完成。欢迎任何建议!
答案 0 :(得分:4)
您可以使用BitConverter
类来代替:
short x = 1;
byte[] bytes = BitConverter.GetBytes(x);
short y = BitConverter.ToInt32(bytes, 0);
这也包含其他整数类型int
和long
的重载。
如果您真的想自己编写代码,可以通过指定unchecked
来避免溢出异常:
public static byte hibyte(short i)
{
unchecked
{
return (byte)(i >> 8);
}
}
public static ushort makeushort(byte hb, byte lb)
{
unchecked
{
return (ushort)((hb << 8) | lb);
}
}
public static short makeshort(byte hb, byte lb)
{
unchecked
{
return (short)((hb << 8) | lb);
}
}
我只是使用BitConverter
;它非常快。但请注意,它始终使用运行代码的计算机的字节顺序。
这是通过BitConverter.IsLittleEndian
报告的。
如果您要转换的数据具有不同的字节顺序,则必须自己完成。
答案 1 :(得分:3)
有几个答案已经注意到BitConverter
类,以及使用unchecked
进行位移和强制转换。我将快速演示第三种选择:“C风格的联合结构”。
[StructLayout(LayoutKind.Explicit)]
struct Converter
{
[FieldOffset(0)]
public ushort UshortValue;
[FieldOffset(0)]
public short ShortValue;
[FieldOffset(0)]
public byte LoByte;
[FieldOffset(1)]
public byte HiByte;
}
然后像这样使用。
ushort test1 = new Converter { ShortValue = -123 }.UshortValue; // 65413
ushort test2 = new Converter { HiByte = 1, LoByte = 100 }.UshortValue; // 356
byte test3 = new Converter { UshortValue = 356 }.LoByte; // 100
与[{1}}相比,它不需要分配临时字节数组。
答案 2 :(得分:1)
正如其他人所提到的,你可以使用BitConverter
类,虽然他们没有考虑实际代码中的字节序(最后只是简单提到):
public static (byte Hibyte, byte Lobyte) GetBytes(short i)
{ // This is my recommendation; it gets both bytes in one call, so it
// may be more efficient.
var bytes = BitConverter.GetBytes(i);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes)
return (bytes[0], bytes[1]);
}
public static (byte Hibyte, byte Lobyte) GetBytes(ushort i)
{ // BitConverter works equally well with unsigned types.
var bytes = BitConverter.GetBytes(i);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes)
return (bytes[0], bytes[1]);
}
public static byte Hibyte(short i)
{ // If you want to use your original schema, here's the hi byte:
if (BitConverter.IsLittleEndian)
return BitConverter.GetBytes()[1];
return BitConverter.GetBytes()[0];
}
public static byte Hibyte(ushort i)
{ // Again, same thing works for ushort
if (BitConverter.IsLittleEndian)
return BitConverter.GetBytes()[1];
return BitConverter.GetBytes()[0];
}
public static short MakeShort(byte hb, byte lb)
{
byte[] bytes = new byte[] { hb, lb };
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToInt16(bytes);
}
public static ushort MakeUShort(byte hb, byte lb)
{
byte[] bytes = new byte[] { hb, lb };
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToUInt16(bytes);
}
而且,虽然其他人提到使用unchecked
,但他们忽略了一个问题:正如您在原始问题中所指出的那样,当对有符号整数类型进行右移时,符号位会重复出现,所以虽然这有效:
public static byte Hibyte(ushort i)
{ return (byte)(i >> 8); }
// No need for unchecked, because result will always fit in one byte and is never < 0.
public static byte Lobyte(ushort i)
{ return (byte)(i & 0xFF); } // No need for unchecked for the same reason.
public static byte LoByte(short i)
{ return (byte)(i & 0xFF); }
// Like above, no need for unchecked for the same reasons; also, bitwise & works the same
// for both signed and unsigned types.
public static ushort MakeUShort(byte hb, byte lb)
{ return (ushort)((hb << 8) | lb); }
// Again, no need for unchecked; result is always 16 bits and never negative.
public static short MakeShort(byte hb, byte lb)
{ unchecked { return (short)((hb << 8) | lb); } }
// This time, we may need unchecked because result may overflow short.
...为签名 short
的高字节提供的代码需要额外的演员:
public static byte Hibyte(short i)
{ unchecked { return (byte)((ushort)i >> 8); } }
// Again, the unchecked is needed, this time because i may be negative,
// which may need to be accounted for when casting to a ushort.
通过在转移之前将签名的short
转换为无符号ushort
,我们会将符号位添加到前面8次。或者,我们可以使用按位&
来忽略传播的符号位:
public static byte HiByte (short i)
{ return (byte)((i >> 8) & 0xFF); }
// Since bitwise operations never result in overflow, and by the time we cast at the end,
// the number is guaranteed to fit in a byte and be >= 0, we no longer need any unchecked blocks.
任何这些都会产生相同的结果。