.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方法
System.IO.BinaryReader.ReadInt64()方法
public virtual long () { this.FillBuffer(8); uint |
但是,.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)這個分支,沒有走下面的程式碼。