您好,登錄后才能下訂單哦!
本篇內容介紹了“C#指針內存控制Marshal內存數據存儲原理是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
1、內存是由 Key 和 Value 組成,Key 是內存地址、Value 是存儲的數據;
2、Key:是一個32位長度的二進制數;(64位的程序則是64位長度的二進制)
> 32位最大值為二進制 ?0111 1111 1111 1111 1111 1111 1111 1111?
或十六進制 0x7FFF FFFF,或十進制 2 147 483 647 (2GB) (int.MaxValue);
> 在C#程序中由 IntPtr 類型進行存儲,常以十六進制數進行交互;
3、Value:則是一個8位長度的二進制數;(所以說計算機只能存儲 0 和 1 就是這原因)
> 最大值為二進制 1111 1111?,或十六進制 0xFF,或十進制 255;
> 也就是 1byte 的數據,所以說計算機最小存儲單位為 byte 也正是如此;
4、內存組成結構如下:
> 二進制:Key (0111 1111 1111 1111 1111 1111 1111 1111?) = Value (1111 1111)
> 十六進制:Key (0x7FFF FFFF) = Value (0xFF)
> 十進制:Key (2 147 483 647) = Value (255)
> 程序:Key (IntPtr) = Value (byte)
1、指針是用于指向一個值類型數據,非想象中的面向過程邏輯、認為第一個讀取后會自動指向下一個,哈哈;
2、如 int 類型的指針,就是將指定內存地址中的數據轉換成 int 數據;
3、由于 int 類型長度為32位(4byte),所以指針讀取數據時會自動取連續4byte的數據來轉換成 int;
> 如一個 int 類型值為 123456,假設他的內存地址為 IntPtr(0x014245E0),那么他所占用的內存塊則為以下:
第1byte:IntPtr(0x014245E0) = byte(0x40)
第2byte:IntPtr(0x014245E1) = byte(0xE2)
第3byte:IntPtr(0x014245E2) = byte(0x01)
第4byte:IntPtr(0x014245E3) = byte(0x00)
組成結構為:IntPtr(0x014245E0) = byte[] { 0x40, 0xE2, 0x01, 0x00 }
> 那么下一個對象則就從 IntPtr(0x014245E4) 開始,如:IntPtr(0x014245E4) = byte[] { 0x00, 0x00, 0x00, 0x00 };
OK,說完原理得開始說代碼了,來個華麗的分割線;
再聲明一下:
1、由于 C# 程序中默認是不允許使用不安全代碼,如內存控制、指針等操作;
2、所以關于非安全操作的代碼需要寫在 unsafe 語句塊中;
3、另外還需要設置允許使用不安全代碼,如:解決方案 > 選擇項目 > 右鍵 > 屬性 > 生成 > [√] 允許不安全代碼;
int val = 10; unsafe { int* p = &val; //&val用于獲取val變量的內存地址,*p為int類型指針、用于間接訪問val變量 *p *= *p; //通過指針修改變量值(執行此操作后 val 變量值將會變成 100) }
string val = "ABC"; unsafe { fixed (char* p = val) //fixed用于禁止垃圾回收器重定向可移動的變量,可理解為鎖定引用類型對象 { *p = 'D'; //通過指針修改變量值(執行此操作后 val 變量值將會變成 "DBC") p[2] = 'E'; //通過指針修改變量值(執行此操作后 val 變量值將會變成 "DBE") int* p2 = (int*)p; //將char類型的指針轉換成int類型的指針 } }
double[] array = { 0.1, 1.5, 2.3 }; unsafe { fixed (double* p = &array[2]) { *p = 0.2; //通過指針修改變量值(執行此操作后 array 變量值將會變成{ 0.1, 1.5, 0.2 }) } }
User val = new User() { age = 25 }; unsafe { fixed (int* p = &val.age) //fixed用于禁止垃圾回收器重定向可移動的變量,可理解為鎖定引用類型對象 { *p = *p + 1; //通過指針修改變量值(執行此操作后 val.age 變量值將會變成 26) } } /* public class User { public string name; public int age; } */
char val = 'A'; unsafe { int valAdd = (int)&val; //獲取val變量的內存地址,并將地址轉換成十進制數 //IntPtr address = (IntPtr)123; //選擇一個內存地址(可以是任何一個變量的內存地址) IntPtr address = (IntPtr)valAdd; //選擇一個內存地址(暫使用val變量的內存地址做測試) byte* p = (byte*)address; //將指定的內存地址轉換成byte類型的指針(如果指定的內存地址不可操的話、那操作時則會報異常“嘗試讀取或寫入受保護的內存。這通常指示其他內存已損壞。”) byte* p2 = (byte*)2147483647; //還可通過十進制的方式選擇內存地址 byte* p3 = (byte*)0x7fffffff; //還可通過十六進制的方式選擇內存地址 *p = (byte)'B'; //通過指針修改變量值(執行此操作后 val 變量值將會變成 'B') }
int valInt = 10; //定義一個int類型的測試val char valChar = 'A'; //定義一個char類型的測試val int* pInt = &valInt; //定義一個int*類型的指針 char* pChar = &valChar; //定義一個char*類型的指針 void* p1 = pInt; //void*可以用于存儲任意類型的指針 void* p2 = pChar; //void*可以用于存儲任意類型的指針 pInt = (int*)p2; //將void*指針轉換成int*類型的指針 (#需要注意一點:因為都是byte數據、所以不會報轉換失敗異常) pChar = (char*)p1; //將void*指針轉換成char*類型的指針(#需要注意一點:因為都是byte數據、所以不會報轉換失敗異常)
unsafe { int* intBlock = stackalloc int[100]; char* charBlock = stackalloc char[100]; }
using System.Runtime.InteropServices; //int length = 1024; //定義需要申請的內存塊大小(1KB) int length = 1024 * 1024 * 1024; //定義需要申請的內存塊大小(1GB) IntPtr address = Marshal.AllocHGlobal(length); //從非托管內存中申請內存空間,并返會該內存塊的地址 (單位:字節) //相當于byte[length] //注意:申請內存空間不會立即在任務管理器中顯示內存占用情況 try { #region Marshal - 寫入 { Marshal.WriteByte(address, 111); //修改第一個byte中的數據 Marshal.WriteByte(address, 0, 111); //修改第一個byte中的數據 Marshal.WriteByte(address, 1, 222); //修改第二個byte中的數據 Marshal.WriteByte(address, length - 1, 255); //修改最后一個byte中的數據 (#此處需要注意,如果定義的偏移量超出則會誤修改其他變量的數據) } #endregion #region Marshal - 讀取 { int offset = length - 1; //定義讀取最后一個byte的內容 byte buffer0 = Marshal.ReadByte(address); //讀取第一個byte中的數據 byte buffer1 = Marshal.ReadByte(address, 0); //讀取第一個byte中的數據 byte buffer2 = Marshal.ReadByte(address, 1); //讀取第二個byte中的數據 byte buffer3 = Marshal.ReadByte(address, length - 1); //讀取最后一個byte中的數據 } #endregion #region Marshal - 數組數據寫入到目標內存塊中 { //source可以是byte[]、也可以是int[]、char[]... byte[] source = new byte[] { 1, 2, 3 }; //將source變量的數組數據拷貝到address內存塊中 Marshal.Copy(source: source, startIndex: 0, //從source的第一個item開始 length: 3, //選擇source的3個item destination: address); //選擇存儲的目標 (會寫到address內存塊的開頭處) } #endregion #region Marshal - 內存塊數據讀取到目標數組中 { //dest可以是byte[]、也可以是int[]、char[]... byte[] dest = new byte[5]; Marshal.Copy(source: address, destination: dest, //#注意:目標數組不能為空、且需要有足夠的空間可接收數據 startIndex: 1, //從dest數組的第二個item開始 length: 3); //將address內存塊的前3個item寫入到dest數組中 } #endregion unsafe { int[] array = new int[5] { 1, 2, 3, 4, 5 }; int* p = (int*)Marshal.UnsafeAddrOfPinnedArrayElement(array, 1); //獲取數組第二個item的內存地址、并轉換成int類型的指針 char* p2 = (char*)Marshal.UnsafeAddrOfPinnedArrayElement(array, 1); //獲取數組第二個item的內存地址、并轉換成char類型的指針 } } finally { Marshal.FreeHGlobal(address); //釋放非托管內存中分配出的內存 (釋放后可立即騰出空間給系統復用) }
“C#指針內存控制Marshal內存數據存儲原理是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。