C#签名/未签名的演员问题

时间:2015-10-27 13:29:30

标签: c#

我需要在有符号整数和它们的内部表示之间转换为一系列字节。在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#中以某种方式(可能简单地)完成。欢迎任何建议!

3 个答案:

答案 0 :(得分:4)

您可以使用BitConverter类来代替:

short x = 1;
byte[] bytes = BitConverter.GetBytes(x);
short y = BitConverter.ToInt32(bytes, 0);

这也包含其他整数类型intlong的重载。

如果您真的想自己编写代码,可以通过指定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.

任何这些都会产生相同的结果。