c#有符号/无符号强制转换问题

本文关键字:转换 问题 无符号 符号 | 更新日期: 2023-09-27 18:19:22

我需要在有符号整数和它们的内部表示之间转换为一系列字节。在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#中,对于有符号数,>>运算符在符号位移位。所有这些使得将我的代码转换为c#变得困难,例如

1) c#函数
public static byte hibyte(short i)
{return (byte) (i>>8);}
如果I为负,

抛出溢出异常

2) c#函数
public static ushort makeshort(byte hb, byte lb)
{return (short) (((ushort)hb << 8) | (ushort)lb); }
如果结果short为负,

抛出溢出异常。在这里(短)hb <<因为移位是在无符号数上进行的。但是我需要将相同的数据解释为有符号整数,我不知道怎么做。我知道对于c#来说,这种类似C的强制转换是作弊,因为正值可能会变成负值,但这正是我真正需要的。用于处理从设备读取的字节流等)目前,我正在使用C代码编译为所有二进制操作的非托管dll,但这不是很优雅,我确信这可以在c#中以某种方式完成(可能简单)。欢迎提出任何建议!

c#有符号/无符号强制转换问题

几个答案已经注意到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;
}

然后使用like so。

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

BitConverter相比,它的优点是不需要分配临时字节数组

您可以使用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报告的。

如果你要转换的数据有不同的端序,你必须自己做

正如其他人所提到的,您可以使用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.