.net的一個Bug:Int64與Byte[8]互轉的問題


有一個64位整數值:

   Int64 n = 634636512000000000;

有兩種方法可以轉爲字節流

1:

   byte[] buffer = new byte[8];
   buffer[0] = (byte)n;
   buffer[1] = (byte)(n >> 8);
   buffer[2] = (byte)(n >> 0x10);
   buffer[3] = (byte)(n >> 0x18);
   buffer[4] = (byte)(n >> 0x20);
   buffer[5] = (byte)(n >> 0x28);
   buffer[6] = (byte)(n >> 0x30);
   buffer[7] = (byte)(n >> 0x38);

2:

   byte[] buffer=BitConverter.GetBytes(n);

兩個方法得到的buffer內容是一樣的。

然後如果再使用buffer向Int64轉回的時候問題出現了。

也有兩個方法:

1:

uint num = (uint)(((buffer[0] | (buffer[1] << 8)) | (buffer[2] << 0x10)) | (buffer[3] << 0x18));
uint num2 = (uint)(((buffer[4] | (buffer[5] << 8)) | (buffer[6] << 0x10)) | (buffer[7] << 0x18));
Int64 t = (Int64)((num2 << 0x20) | num);

得到的t值是2614029963???

2:

Int64 t = BitConverter.ToInt64(buffer, 0);

得到t的值是634636512000000000,對!

方法1大家可以從.net源碼中找到,比如就在BitConvert.ToInt64方法中存在,或System.IO.BinaryReader類的ReadInt64()方法可以得到。比如源碼:

BitConvert.ToInt64方法

public static unsafe long ToInt64(byte[] value, int startIndex)
{
    if (value == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
    }
    if (((ulong) startIndex) >= value.Length)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
    }
    if (startIndex > (value.Length - 8))
    {
        ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
    }
    fixed (byte* numRef = &(value[startIndex]))
    {
        if ((startIndex % 8) == 0)
        {
            return *(((long*) numRef));
        }
        if (IsLittleEndian)
        {
            int num = ((numRef[0] | (numRef[1] << 8)) | (numRef[2] << 0x10)) | (numRef[3] << 0x18);
            int num2 = ((numRef[4] | (numRef[5] << 8)) | (numRef[6] << 0x10)) | (numRef[7] << 0x18);
            return (((long) ((ulong) num)) | (num2 << 0x20));
        }
        int num3 = (((numRef[0] << 0x18) | (numRef[1] << 0x10)) | (numRef[2] << 8)) | numRef[3];
        int num4 = (((numRef[4] << 0x18) | (numRef[5] << 0x10)) | (numRef[6] << 8)) | numRef[7];
        return (((long) ((ulong) num4)) | (num3 << 0x20));
    }
}

 

 System.IO.BinaryReader.ReadInt64()方法

public virtual long ReadInt64()
{
    this.FillBuffer(8);
    uint num = (uint) (((this.m_buffer[0] | (this.m_buffer[1] << 8)) | (this.m_buffer[2] << 0x10)) | (this.m_buffer[3] << 0x18));
    uint num2 = (uint) (((this.m_buffer[4] | (this.m_buffer[5] << 8)) | (this.m_buffer[6] << 0x10)) | (this.m_buffer[7] << 0x18));
    return (long) ((num2 << 0x20) | num);
}

 

但是,.net裏面的源代碼錯了!!!

哪裏錯了呢,其實思路是對的,在數量的值小於一個整數的時候也是對的。

問題就出在了最後一個return上。

本來num2應該左移0x20個位,然後成爲64位數的高32位,然而這裏的num2卻是一個uint類型。UInt類型左移後出現的結果仍然是一個UInt的值。

兩個UInt的值或(|)後仍是一個UInt,哪裏是long呢?

微軟在這裏犯了一個錯誤,因爲本來正確的代碼應該是這樣的:

public virtual long ReadInt64()
{
	this.FillBuffer(8);
	uint num = (uint)(((this.m_buffer[0] | (this.m_buffer[1] << 8)) | (this.m_buffer[2] << 0x10)) | (this.m_buffer[3] << 0x18));
	uint num2 = (uint)(((this.m_buffer[4] | (this.m_buffer[5] << 8)) | (this.m_buffer[6] << 0x10)) | (this.m_buffer[7] << 0x18));
	return (((long)num2 << 0x20) | num);
}

(long)的位置放錯了,或者說,沒注意到num2應該使用(Long)轉換一下。這是導致錯誤的原因。

 那爲什麼BitConvert.ToInt64是對的呢,嘿嘿,因爲在本文中它走了if ((startIndex %8) == 0)這個分支,沒有走下面的代碼。

發佈了83 篇原創文章 · 獲贊 29 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章