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
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow