C# Language
Wskaźniki
Szukaj…
Uwagi
Wskaźniki i unsafe
Ze względu na swój charakter wskaźniki generują kod niezweryfikowany. Dlatego użycie dowolnego typu wskaźnika wymaga unsafe
kontekstu.
Typ System.IntPtr
to bezpieczne opakowanie wokół void*
. Jest pomyślany jako wygodniejsza alternatywa dla void*
gdy niebezpieczny kontekst nie jest wymagany do wykonania danego zadania.
Nieokreślone zachowanie
Podobnie jak w C i C ++, nieprawidłowe użycie wskaźników może wywołać niezdefiniowane zachowanie, z możliwymi skutkami ubocznymi jest uszkodzenie pamięci i wykonanie niezamierzonego kodu. Ze względu na niemożliwy do zweryfikowania charakter większości operacji na wskaźnikach, prawidłowe użycie wskaźników jest całkowicie obowiązkiem programisty.
Typy obsługujące wskaźniki
W przeciwieństwie do C i C ++, nie wszystkie typy C # mają odpowiadające typy wskaźników. Typ T
może mieć odpowiedni typ wskaźnika, jeśli spełnione są oba poniższe kryteria:
-
T
jest typem struktury lub typem wskaźnika. -
T
zawiera tylko elementy spełniające oba te kryteria rekurencyjnie.
Wskaźniki dostępu do tablicy
Ten przykład pokazuje, w jaki sposób można używać wskaźników do dostępu typu C do tablic C #.
unsafe
{
var buffer = new int[1024];
fixed (int* p = &buffer[0])
{
for (var i = 0; i < buffer.Length; i++)
{
*(p + i) = i;
}
}
}
unsafe
słowo kluczowe jest wymagane, ponieważ dostęp do wskaźnika nie wyemituje żadnych kontroli granic, które są normalnie emitowane podczas regularnego uzyskiwania dostępu do tablic C #.
fixed
słowo kluczowe informuje kompilator C # o wysłaniu instrukcji, aby przypiąć obiekt w sposób bezpieczny dla wyjątków. Przypinanie jest wymagane, aby zapewnić, że śmieciarz nie przeniesie tablicy do pamięci, ponieważ spowodowałoby to unieważnienie wszelkich wskaźników wskazujących w tablicy.
Wskaźnik arytmetyczny
Dodawanie i odejmowanie wskaźników działa inaczej niż liczby całkowite. Gdy wskaźnik jest zwiększany lub zmniejszany, adres, na który wskazuje, jest zwiększany lub zmniejszany o rozmiar typu odniesienia.
Na przykład typ int
(alias dla System.Int32
) ma rozmiar 4. Jeśli int
można zapisać w adresie 0, kolejne int
można zapisać w adresie 4 i tak dalej. W kodzie:
var ptr = (int*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 4
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8
Podobnie typ long
(alias dla System.Int64
) ma rozmiar 8. Jeśli long
może być przechowywany w adresie 0, kolejny long
może być przechowywany w adresie 8 i tak dalej. W kodzie:
var ptr = (long*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 16
Typ void
jest specjalny, a wskaźniki void
są również specjalne i są używane jako wskaźniki typu catch-all, gdy typ nie jest znany lub nie ma znaczenia. Ze względu na ich zależny od wielkości charakter, void
wskaźników nie można zwiększać ani zmniejszać:
var ptr = (void*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));
Gwiazdka jest częścią tego typu
W C i C ++ gwiazdka w deklaracji zmiennej wskaźnikowej jest częścią deklarowanego wyrażenia . W języku C # gwiazdka w deklaracji jest częścią typu .
W C, C ++ i C # następujący fragment deklaruje wskaźnik int
:
int* a;
W C i C ++ poniższy fragment deklaruje wskaźnik int
i zmienną int
. W języku C #, to deklaruje dwie int
wskazówek:
int* a, b;
W C i C ++, następujący fragment deklaruje dwie int
wskazówek. W języku C # jest niepoprawny:
int *a, *b;
unieważnić*
C # dziedziczy po C i C ++ użycie void*
jako wskaźnika agnostycznego i typu agnostycznego.
void* ptr;
Dowolny typ wskaźnika można przypisać do void*
za pomocą niejawnej konwersji:
int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
Odwrotna sytuacja wymaga jawnej konwersji:
int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
int* p2 = (int*)ptr;
Dostęp członków za pomocą ->
C # dziedziczy po C i C ++ użycie symbolu ->
jako środka dostępu do członków instancji za pomocą pisanego wskaźnika.
Rozważ następującą strukturę:
struct Vector2
{
public int X;
public int Y;
}
To jest przykład użycia ->
do uzyskania dostępu do jego członków:
Vector2 v;
v.X = 5;
v.Y = 10;
Vector2* ptr = &v;
int x = ptr->X;
int y = ptr->Y;
string s = ptr->ToString();
Console.WriteLine(x); // prints 5
Console.WriteLine(y); // prints 10
Console.WriteLine(s); // prints Vector2
Ogólne wskaźniki
Kryteria, które typ musi spełniać, aby obsługiwać wskaźniki (patrz Uwagi ), nie mogą być wyrażone w kategoriach ogólnych ograniczeń. Dlatego każda próba zadeklarowania wskaźnika do typu podanego za pomocą parametru typu ogólnego zakończy się niepowodzeniem.
void P<T>(T obj)
where T : struct
{
T* ptr = &obj; // compile-time error
}