C# 中的指標使用
C#為了型別安全,預設並不支援指標。但是也並不是說C#不支援指標,我們可以使用unsafe關鍵詞,開啟不安全程式碼(unsafe code)開發模式。在不安全模式下,我們可以直接操作記憶體,這樣就可以使用指標了。在不安全模式下,CLR並不檢測unsafe程式碼的安全,而是直接執行程式碼。unsafe程式碼的安全需要開發人員自行檢測。
一、Vs2010中開啟unsafe code 的方式
在方法、類、程式碼塊中使用unsafe關鍵詞,如:
unsafe static void Main(string[] args){ //程式碼} unsafe { //程式碼塊 }
然後再專案上點選滑鼠右鍵,選擇“屬性”,在“生成”選項卡中選中“允許不安全程式碼”
二、C#可以定義為指標的型別有
sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool,
struct(結構體),結構體中只能包括非託管型別。
三、C#指標操作符
操作符 | 說明 |
* | 取值運算子 |
& | 取址運算子 |
-> | 通過指標處理結構體中的資料(獲取或賦值) |
++與– | 指標增、減操作 |
fixed | 使用者暫時固定託管程式碼中引用型別的位置。 |
Stackallc | 分配記憶體 |
例如分配記憶體
char* cptr = stackallocchar[26]; for (int i = 0; i < 26;i++ ) { cptr[i] = (char) (i+65); } for (int i = 0; i < 26;i++ ) { Console.WriteLine(string.Format("{0}:{1}",(int)&cptr[i],cptr[i])); }
至於釋放記憶體,我在msdn上搜索了一下,c#並沒有提供釋放記憶體的函式。而msdn給的解釋是:分配的記憶體會在方法結束後自動釋放。
fixed的應用會在下面的類與指標中做說明。
四、C#指標的定義
定義指標 | 說明 |
int* p | 整形指標 |
int** p | 指向整形指標的指標 |
char* c | 指向字元的指標 |
int*[] arr | 整形一維陣列指標 |
五、指標的使用
1.整形指標的使用
int i=10; int* iptr = &i; //將i的地址賦值給iptr Console.WriteLine((int)&iptr); //取iptr得地址 Console.WriteLine(*iptr); //取iptr指向的值
2.結構體指標
struct Location { public int X; public int Y; } unsafe static void Main(string[] args) { Location location; location.X = 10; location.Y = 5; Location* lptr = &location; Console.WriteLine(string.Format("location 地址{0},lptr地址{1},lptr值{2}",(int)&location,(int)lptr,*lptr)); Console.WriteLine(string.Format("location.x的地址{0},location.x的值{1}",(int)&(lptr->X),lptr->X)); Console.WriteLine(string.Format("location.y的地址{0},location.y的值{1}", (int)&(lptr->Y), lptr->Y)); }
以上程式碼輸出結構體的地址和值。我們在操作地址時,可以直接看到結構體的記憶體分配。
3.指標與引數
public static unsafe int* Add(int* x,int* y) { int sum = *x + *y; return ∑ } int i = 2, j = 3; Console.WriteLine(*Add(&i,&j));
4.類與指標,因為類是託管型別,我們知道類受到“垃圾收集”的影響,它的記憶體地址是不固定的。而且類是引用型別,是不能宣告為指標型別的。而指標分配記憶體後,不受“垃圾收集”影響,地址是固定的。所以為了使用類中的資料,我們需要臨時固定類的地址。這就用到fixed關鍵詞,用fixed後,就可以操作類中的值型別了。
class People { public int Age; //值型別,不可以是屬性 public void ShowAge() { Console.WriteLine(Age); } } People people = new People(); people.Age = 10; fixed(int* agePtr=&people.Age) { *agePtr += 1; } people.ShowAge(); //11
通過以上的方法,我們可以操作值型別,也可以獲得值型別的地址。但如何獲取類的記憶體地址?我們可以使用GCHandle,來自System.Runtime.InteropServices名稱空間。GCHandle提供從非託管記憶體訪問託管物件的方法。如下:
object p = new People(); GCHandle h = GCHandle.Alloc(p, GCHandleType.Pinned); IntPtr addr = h.AddrOfPinnedObject(); Console.WriteLine(addr.ToString()); h.Free();
六、C#中使用指標的總結
1.引用型別不能定義為指標
2.msdn上說enum可以定義為指標,可是我真不知道它的用處是什麼。所以在指標的型別中並沒有出現enum型別。
3.c#中的指標操作遠遠不如c/c++,如果想學習指標的話,還是用c/c++
4.微軟並不推薦使用unsafe code模式,也不推薦使用指標。在msdn官方文件中,唯一一句讚美C#指標的話就是“合理的使用指標,可以提高程式的執行速度”。但是什麼是“合理的使用”?我下載了msdn上的幾個關於C#指標的例項程式碼,發現用的最多的是呼叫api函式,在api函式中,有大量的指標引數。
5.fixed的使用可能產生儲存碎片,因為它們不能移動。如果確實需要固定物件,固定物件的時間應該越短越好。
6.可以使我們瞭解非託管型別的記憶體分配。