更新:一篇更全面的文章:http://www.cnblogs.com/mgen/archive/2011/11/28/2266010.html
註:所有樣本均擬在Little Endian環境下運行
在C/C++中,操作原始(預定義)資料類型的記憶體位元組分布很簡單,我們可以使用指標來這樣做:
/****************************
* C/C++ 代碼
****************************/
int i = 0xAA00FF;
//i的記憶體儲存形式:FF 00 AA 00
unsigned char *p = (unsigned char*)&i;
for(int i = 0; i < sizeof(i); i++)
cout<<hex<<uppercase<<setw(2)<<setfill('0')<<(int)(*p++)<<" ";
//換行
puts("");
//輸出:FF 00 AA 00
//注意p在迴圈後的位置,p-3 指向變數i在記憶體中第二個位元組
*(p-3) = 0xCC;
//現在記憶體儲存形式:FF CC AA 00
//i變成xAACCFF = 11193599
cout << dec << i << endl;
//輸出:11193599
但是在C#中,這樣的操作是預設不被允許的,你可以手動允許編譯unsafe代碼來增加指標操作,或者用位操作運算子來進行類似操作,那麼有沒有更好得辦法呢?
答案就是System.Buffer類。
簡而言之Buffer類是以連續位元組流的形式操作未經處理資料類型,未經處理資料類型(Primitive types,這個在MSDN的翻譯是“基元類型”,我覺得未經處理資料類型更恰當)是指:Boolean、Char、SByte、Byte、Int16、UInt16、Int32、UInt32、Int64、UInt64、IntPtr、UIntPtr、Single 和Double,他們都是結構體且在記憶體中分布連續。
(System.Buffer類MSDN:http://msdn.microsoft.com/zh-cn/library/system.buffer.aspx )
那麼上述C/C++代碼在C#下(更確切地說是在.NET下),用Buffer類的GetByte和SetByte方法也可以輕鬆實現,相比上面提到的其他方法,他的優勢是不用開啟unsafe編譯同樣相比位操作實現起來更簡單。當然資料必須以數組形式儲存,由於Buffer只針對未經處理資料類型的數組而進行操作。
C#代碼:
/****************************
* C# 代碼
****************************/
int[] arr = new int[] { 0xAA00FF };
//記憶體儲存形式:FF 00 AA 00
for (int i = 0; i < sizeof(int); i++)
{
Console.Write("{0:X2} ", Buffer.GetByte(arr, i));
}
//換行
Console.WriteLine();
Buffer.SetByte(arr, 1, 0xCC);
//修改成:FF CC AA 00
//arr[0] = 0xAACCFF = 11193599
Console.WriteLine(arr[0]);
輸出和C/C++結果一樣:
FF 00 AA 00
11193599
而且C#代碼看起來更簡潔。
Buffer類的其他用法
除了GetByte和SetByte,Buffer類還有兩個方法。
ByteLength:返回未經處理資料類型數組的位元組數。
BlockCopy:以連續位元組流的方式對數組進行拷貝。這個方法相對Array.Copy或者ICollection.CopyTo方法的優勢就是速度快,因為他不考慮資料類型,直接按位元組拷貝,當然還是強調一下,只能針對未經處理資料類型。