2013-08-11 16 views
5

Wiem, używając flagi /unsafe w języku C#, można użyć wskaźników. W C/C++, aby usunąć wskaźnik, należy użyć odpowiednio: free(pointer); i delete pointer;. Jak jednak osiągnąć ten sam efekt za pomocą wskaźników C#?Usuwanie C# Niebezpiecznych wskaźników

+10

Jak zwykle, naprawdę się nie zgadzam. Dobre, krótkie pytanie, bardzo jasne i bardzo przydatne. Odpowiedź jest świetna. Ktoś naprawdę musi rządzić w "komitecie". – johnrubythecat

Odpowiedz

25

To zależy. Używasz free i delete do zwolnienia pamięci przydzielonej malloc i new.

ale

w ogóle, jeśli nie wezwanie pinvoke, wówczas wskaźnik powinien być IntPtr.

jeśli używasz fixed (lub GCHandle), aby uzyskać wskaźnik do zarządzanego obiektu, a następnie pamięć została przydzielona z pamięci GC

  • Dla pamięci GC, kiedy un-pinowego, że pamięć (wyjść z bloku fixed lub zwolnić GCHandle), GC powróci posługiwaniu się nim
  • dla pamięci przydzielonej przez .NET Marshal metod użyć metodę uzupełniającą Free
  • dla pamięci otrzymane od metod natywnych, trzeba użyć " corr ect "natywna metoda, aby ją uwolnić.

Przykład przypinanie pamięci otrzymanego przez NET:

int[] arr = new int[5]; 

fixed (int* p = arr) 
{ 
    // here arr is fixed in place and it won't be freed/moved by gc 
} 

// here arr is un-fixed and the GC will manage it 

lub prawie równa (ale nieco mniej bezpieczne, ponieważ odpinania jest wykonywane ręcznie)

GCHandle handle = GCHandle.Alloc(arr, GCHandleType.Pinned); 

int* p2 = (int*)handle.AddrOfPinnedObject(); 

// here arr is fixed in place and it won't be freed/moved by gc 

handle.Free(); 
// here arr is un-fixed and the GC will manage it 

Przykład przydzielanie pewnej ilości pamięci z "natywnej" puli (przez alokator normalnie używany przez obiekty COM) za pomocą Marshal.AllocCoTaskMem (zauważ, że Marshal.AllocCoTaskMem wywołuje CoTaskMemAlloc interfejsu Windows API, dzięki czemu można używać zarówno Marshal.FreeCoTaskMem i Windows API CoTaskMemFree, aby go uwolnić):

// allocating space for 1000 chars 
char* p3 = (char*)Marshal.AllocCoTaskMem(1000 * sizeof(char)); 

// here you can use p3 

// and here you free it 
Marshal.FreeCoTaskMem((IntPtr)p3); 

lub z innym podzielnika obsługiwanym przez Marshal (to jest normalnie używany przez Windows API):

// allocating space for 1000 chars 
char* p4 = (char*)Marshal.AllocHGlobal(1000 * sizeof(char)); 

// here you can use p4 

// and here you free it 
Marshal.FreeHGlobal((IntPtr)p4); 

Powiedzmy masz niektóre Native kod, który daje dostęp do pewnego pamięci gdzie zapisuje pewne dane:

static extern IntPtr GetSomeMemoryFromSomeWinApi(); 

static extern void FreeSomeMemoryFromSomeWinApi(IntPtr ptr); 

użyć tak:

IntPtr p5 = GetSomeMemoryFromSomeWinApi(); 

// here you have some memory received from some native API 

// and here you free it 
FreeSomeMemoryFromSomeWinApi(p5); 

W tym przypadku jest to biblioteka, która ma dać metodę Free, bo nie wiem jak pamięć została przydzielona, ​​ale czasami dokumentacji biblioteki mówi, że pamięć jest alokowana przez konkretnego podzielnika , więc używasz tego typu deallocator, aby go zwolnić, jak gdyby API był jakimś obiektem COM.

Klasa Marshal nawet ma podzielnik dla BSTR (Unicode używane przez obiekty COM. Mają długość wstępnie pendend)

string str = "Hello"; 
char *bstr = (char*)Marshal.StringToBSTR(str); 

Marshal.FreeBSTR((IntPtr)bstr); 

Mają specjalnego traktowania, ponieważ ich „prawdziwe” start adres jest jak (bstr - 2) (mieli już przedrostek Int32 wraz z ich długością)

Chodzi o to, że jest tak dużo podzielników, jak ziarno piasku pustyni i gwiazdy nieba. Każdy z nich (z wyjątkiem standardowego .NET, używanego przez new) ma odpowiedni deallocator. Idą jak mąż i żona. Nie mieszają się z innymi.

W końcowej nocie, jeśli piszesz mieszany .NET/natywną C lub kod C++, będziesz musiał narazić niektóre C/C++ metod, które wywołują ichfree/delete, ponieważ ich free/delete są częścią ich biblioteki C/C++, a nie systemu operacyjnego.

+2

Uwielbiam odpowiedź! – Titus

+0

Wszystko tutaj jest wspaniałe, z wyjątkiem sugestii, że wskaźniki PInvoke powinny być inttrtr. Znacznie bezpieczniej jest użyć niebezpiecznej struktury * i pozwolić kompilatorowi na sprawdzenie typów wskaźników. IntPtr to luka umożliwiająca VB i rzekomo "bezpieczny" kod do robienia bardzo niebezpiecznych rzeczy poprzez traktowanie parametrów PInvoke jako IntPtr/void *. –

Powiązane problemy