Czym dokładnie jest IntPtr?


171

Korzystając z IntelliSense i patrząc na kod innych osób, natknąłem się na ten IntPtrtyp; za każdym razem, gdy trzeba było go użyć, po prostu wstawiłem nulllub IntPtr.Zeroi stwierdziłem, że większość funkcji działa. Co to właściwie jest i kiedy / dlaczego jest używane?

Odpowiedzi:


158

Jest to „natywna (specyficzna dla platformy) liczba całkowita”. Jest wewnętrznie reprezentowany jako void*liczba całkowita, ale jest ujawniany. Możesz go używać, gdy potrzebujesz przechowywać niezarządzany wskaźnik i nie chcesz używać unsafekodu. IntPtr.Zerojest efektywnie NULL(pusty wskaźnik).


53
Wskaźnik to coś, co wskazuje na adres w pamięci. W językach zarządzanych masz odwołania (adres może się poruszać), aw językach niezarządzanych masz wskaźniki (adres jest naprawiony)
Colin Mackay

93
Ogólnie (w językach programowania) wskaźnik to liczba reprezentująca fizyczne miejsce w pamięci. Zerowy wskaźnik jest (prawie zawsze), który wskazuje na 0, i jest powszechnie uznawany za „nie wskazuje na nic”. Ponieważ systemy mają różne ilości obsługiwanej pamięci, przechowywanie tej liczby nie zawsze zajmuje taką samą liczbę bajtów, dlatego nazywamy „liczbą całkowitą o rodzimym rozmiarze”, która może przechowywać wskaźnik w dowolnym konkretnym systemie.
Sam Harwell

5
+1 dla tego komentarza @ 280Z28, to najbardziej zwięzłe wyjaśnienie wskazówek, jakie kiedykolwiek widziałem.
BlueRaja - Danny Pflughoeft

9
Nazywa się IntPtr, ponieważ aby użyć go z niezarządzanego kodu natywnego C / C ++, będziesz musiał użyć typu analogicznego: intptr_t. C #'s IntPtr mapuje dokładnie na intptr_t C / C ++. I prawdopodobnie jest zaimplementowany jako intptr_t. W C / C ++ typ intptr_t ma taką samą wielkość jak typ void *.
Петър Петров

2
@Trap „Typ specyficzny dla platformy, który jest używany do reprezentowania wskaźnika lub uchwytu”. Nie jest to „liczba całkowita specyficzna dla platformy”, „sposób reprezentowania wskaźnika lub uchwytu specyficzny dla platformy”. IntPtrma sens, ponieważ jest to liczba całkowita (jak w matematycznej liczbie całkowitej) i wskaźnik (jak we wskaźniku). IntCzęść ma niewiele wspólnego z typem int - to pojęcie, a nie specyficzny typ. Chociaż szczerze mówiąc, jedyną rzeczą, o której mówi specyfikacja CLI, jest to, że jest to rzeczywiście „całkowity, natywny rozmiar”. No cóż :)
Luaan

69

Jest to typ wartości wystarczająco duży, aby przechowywać adres pamięci używany w kodzie natywnym lub niebezpiecznym, ale nie można go bezpośrednio używać jako adresu pamięci w bezpiecznym kodzie zarządzanym.

Możesz użyć, IntPtr.Sizeaby dowiedzieć się, czy pracujesz w procesie 32-bitowym, czy 64-bitowym, ponieważ będzie to odpowiednio 4 lub 8 bajtów.


16
Dobra uwaga na temat wykrywania rozmiaru przestrzeni adresowej dla procesu.
Noldorin

@Noldorin Prawda, ale niekoniecznie niezawodna. W przeszłości istniało wiele architektur, które miały wiele typów wskaźników, aw systemie Windows IntPtrjest również używany do reprezentowania uchwytów, które są 32-bitowe, niezależnie od architektury (choć Sizew tym przypadku nadal mówi 8). Specyfikacja CLI mówi tylko, że jest to liczba całkowita o „rodzimym rozmiarze”, ale tak naprawdę to niewiele mówi.
Luaan,

@Luaan, który tak naprawdę niczego nie zmienia w mojej odpowiedzi, prawda? IntPtr jest tak zwany (i jest używany w źródle CLR) jako wartość wystarczająco duża, aby pomieścić adres pamięci. Oczywiście może mieć mniejsze wartości. Niektóre architektury mają wiele typów wskaźników, ale musi mieć taki, który jest największym z zestawu.
Daniel Earwicker,

@DanielEarwicker O ile wiem, to nie jest problem z żadną obecną implementacją .NET. Jednak problem (historyczny) nie dotyczy tylko rozmiaru - różne wskaźniki mogą być całkowicie niezgodne. W przykładzie bliższym dzisiejszemu PAE używałby adresów 64-bitowych, mimo że „natywny rozmiar wskaźnika” był nadal 32-bitowy. Wraca aż do argumentu „co tak naprawdę oznacza„ system 32-bitowy ”?” Mamy teraz 256-bitowe i 512-bitowe rejestry numeryczne, ale nadal nazywamy to 64-bitowymi. Nawet jeśli w rzeczywistości nie możesz adresować 64-bitowej pamięci fizycznej za pomocą 64-bitowych „wskaźników”. To bałagan.
Luaan,

1
Niektóre z nich są skomplikowane, ale IntPtr nadal jest „typem wartości wystarczająco dużym, aby przechowywać adres pamięci”. Nie jest tak duży jak największy rejestr sprzętu, żeby wziąć jeden z przykładów. Po prostu nie do tego służy. Jest wystarczająco duży, aby reprezentować adres pamięci. Są też takie rzeczy: stackoverflow.com/questions/12006854/… gdzie mamy słowo „wskaźnik” używane do czegoś innego niż adres pamięci - dlatego właśnie powiedziałem „adres pamięci”.
Daniel Earwicker,

48

Oto przykład:

Piszę program w C #, który łączy się z szybkim aparatem. Aparat ma własny sterownik, który pobiera zdjęcia i automatycznie ładuje je do pamięci komputera.

Więc kiedy jestem gotowy, aby wprowadzić najnowszy obraz do mojego programu do pracy, sterownik aparatu dostarcza mi IntPtr, gdzie obraz jest JUŻ przechowywany w pamięci fizycznej, więc nie muszę tracić czasu / zasobów na tworzenie kolejnego blok pamięci do przechowywania obrazu, który jest już w pamięci. IntPtr po prostu pokazuje mi, gdzie obraz już jest.


7
Czyli IntPtr w prosty sposób umożliwia użycie niezarządzanego wskaźnika (takiego jak ten używany w sterowniku aparatu) w kodzie zarządzanym?
Callum Rogers

5
Tak. Cóż, w tym przypadku najprawdopodobniej sterownik aparatu używa niezarządzanych sterowników pod maską, ale aby poprawnie działać w świecie Managed-only, zapewnia IntPtr, aby umożliwić mi bezpieczną pracę z danymi.
bufferz

3
Więc dlaczego nie zwróci ci strumienia (tablicy bajtów)? Dlaczego zwrócenie wartości jest niebezpieczne lub niemożliwe?
The Muffin Man

35

Bezpośrednia interpretacja

IntPtr jest liczbą całkowitą , która ma taką samą wielkość jak wskaźnik .

Możesz użyć IntPtr do przechowywania wartości wskaźnika w typie innym niż wskaźnik. Ta funkcja jest ważna w .NET, ponieważ używanie wskaźników jest bardzo podatne na błędy, a zatem w większości kontekstów jest niedozwolone. Zezwalając na przechowywanie wartości wskaźnika w „bezpiecznym” typie danych, przepływ między niebezpiecznymi segmentami kodu może być realizowany w bezpieczniejszym kodzie wysokiego poziomu - lub nawet w języku .NET, który nie obsługuje bezpośrednio wskaźników.

Rozmiar IntPtr zależy od platformy, ale ten szczegół rzadko musi być brany pod uwagę, ponieważ system automatycznie użyje prawidłowego rozmiaru.

Nazwa „IntPtr” jest myląca - coś takiego jak Handlemogłoby być bardziej odpowiednie. Moje początkowe przypuszczenie było takie, że „IntPtr” był wskaźnikiem do liczby całkowitej. Dokumentacja MSDN dotycząca IntPtr zawiera nieco tajemnicze szczegóły, nigdy nie dostarczając zbyt wielu informacji na temat znaczenia nazwy.

Alternatywna perspektywa

An IntPtrjest wskaźnikiem z dwoma ograniczeniami:

  1. Nie można jej bezpośrednio wyłuskać
  2. Nie zna typu danych, na które wskazuje.

Innymi słowy, an IntPtrjest podobny do a void*- ale z dodatkową funkcją, której może (ale nie powinien) być używany do podstawowych działań arytmetycznych wskaźników.

Aby wyłuskać odwołanie IntPtr, możesz albo rzutować go na prawdziwy wskaźnik (operację, która może być wykonywana tylko w "niebezpiecznych" kontekstach) lub możesz przekazać ją do procedury pomocniczej, takiej jak te dostarczane przez InteropServices.Marshalklasę. Używanie Marshalklasy daje iluzję bezpieczeństwa, ponieważ nie wymaga przebywania w jawnym kontekście „niebezpiecznego”. Nie eliminuje jednak ryzyka awarii, które jest nieodłącznie związane ze stosowaniem wskaźników.


2
Istnieje pewien precedens dla nazwy „intptr” ze standardu C99 języka programowania „C”. linux.die.net/man/3/intptr_t .
Brent Bradburn

17

Co to jest wskaźnik?

We wszystkich językach, wskaźnik jest typem zmiennej, która przechowuje adres pamięci, a można też poprosić ich, aby powiedzieć wam adres są wskazującego lub wartości pod adresem są wskazującego.

Wskaźnik można traktować jako rodzaj zakładki. Z wyjątkiem tego, że zamiast szybkiego przeskakiwania do strony w książce, wskaźnik służy do śledzenia lub mapowania bloków pamięci.

Wyobraź sobie pamięć twojego programu dokładnie jako jedną dużą tablicę 65535 bajtów.

Wskaźniki wskazują posłusznie

Wskaźniki pamiętają po jednym adresie pamięci, dlatego każdy z nich wskazuje na pojedynczy adres w pamięci.

Jako grupa, wskaźniki zapamiętują i przywołują adresy pamięci, wykonując każde polecenie do znudzenia.

Jesteś ich królem.

Wskaźniki w C #

W szczególności w języku C # wskaźnik jest zmienną całkowitą, która przechowuje adres pamięci od 0 do 65534.

Również specyficzne dla języka C # wskaźniki są typu int i dlatego są podpisane.

Nie możesz jednak używać adresów ponumerowanych ujemnie, ani nie możesz uzyskać dostępu do adresu powyżej 65534. Każda próba zrobienia tego spowoduje zgłoszenie wyjątku System.AccessViolationException.

Wskaźnik o nazwie MyPointer jest zadeklarowany w następujący sposób:

int * MyPointer;

Wskaźnik w C # to int, ale adresy pamięci w C # zaczynają się od 0 i rozciągają się aż do 65534.

Ze spiczastymi przedmiotami należy obchodzić się ze szczególną ostrożnością

Słowo niebezpieczne ma cię przestraszyć i nie bez powodu: wskaźniki są spiczastymi przedmiotami, a spiczastymi przedmiotami, takimi jak miecze, topory, wskaźniki itp. Należy obchodzić się ze szczególną ostrożnością.

Wskaźniki dają programiście ścisłą kontrolę nad systemem. Dlatego popełnione błędy mogą mieć poważniejsze konsekwencje.

Aby używać wskaźników, niebezpieczny kod musi być włączony we właściwościach programu, a wskaźniki muszą być używane wyłącznie w metodach lub blokach oznaczonych jako niebezpieczne.

Przykład niebezpiecznego bloku

unsafe
{
    // Place code carefully and responsibly here.

}

Jak używać wskaźników

Gdy zmienne lub obiekty są deklarowane lub tworzone, są one przechowywane w pamięci.

  • Zadeklaruj wskaźnik, używając przedrostka symbolu *.

int *MyPointer;

  • Aby uzyskać adres zmiennej, użyj przedrostka symbolu &.

MyPointer = &MyVariable;

Po przypisaniu adresu do wskaźnika obowiązują następujące zasady:

  • Bez prefiksu *, aby odwołać się do adresu pamięci, który jest wskazywany jako int.

MyPointer = &MyVariable; // Set MyPointer to point at MyVariable

  • Z przedrostkiem *, aby uzyskać wartość przechowywaną pod wskazanym adresem pamięci.

"MyPointer is pointing at " + *MyPointer;

Ponieważ wskaźnik jest zmienną, która przechowuje adres pamięci, ten adres pamięci może być przechowywany w zmiennej wskaźnikowej.

Przykład ostrożnego i odpowiedzialnego wykorzystania wskaźników

    public unsafe void PointerTest()
    {
        int x = 100; // Create a variable named x

        int *MyPointer = &x; // Store the address of variable named x into the pointer named MyPointer

        textBox1.Text = ((int)MyPointer).ToString(); // Displays the memory address stored in pointer named MyPointer

        textBox2.Text = (*MyPointer).ToString(); // Displays the value of the variable named x via the pointer named MyPointer.

    }

Zwróć uwagę, że typ wskaźnika to int. Dzieje się tak, ponieważ C # interpretuje adresy pamięci jako liczby całkowite (int).

Dlaczego jest to int zamiast uint?

Nie ma dobrego powodu.

Po co używać wskaźników?

Wskaźniki to świetna zabawa. Ponieważ tak duża część komputera jest kontrolowana przez pamięć, wskaźniki dają programiście większą kontrolę nad pamięcią programu.

Monitorowanie pamięci.

Użyj wskaźników do odczytywania bloków pamięci i monitorowania zmian wskazywanych wartości w czasie.

Zmień te wartości odpowiedzialnie i śledź, jak zmiany wpływają na Twój komputer.


2
65534wydaje się bardzo błędny jako zakres wskaźnika. Powinieneś podać odniesienie.
Brent Bradburn

1
Głosowałem za tym, ponieważ jest to świetny artykuł wyjaśniający wskazówki. Chciałbym zobaczyć odniesienia do specyfikacji, o których wspomniałeś (jak skomentowałem powyżej). Jednak; ta odpowiedź nie ma nic wspólnego z pytaniem. Pytanie dotyczyło System.IntPtr. Chciałbym zobaczyć zaktualizowaną odpowiedź na końcu wyjaśniającą, co również System.IntPtri jak odnosi się do niebezpiecznych wskaźników w C # proszę.
Michael Puckett II

7

MSDN mówi nam:

Typ IntPtr został zaprojektowany jako liczba całkowita, której rozmiar jest specyficzny dla platformy. Oznacza to, że instancja tego typu powinna być 32-bitowa na 32-bitowym sprzęcie i systemach operacyjnych oraz 64-bitowa na 64-bitowym sprzęcie i systemach operacyjnych.

Typ IntPtr może być używany przez języki obsługujące wskaźniki oraz jako powszechny sposób odwoływania się do danych między językami, które obsługują i nie obsługują wskaźników.

Obiekty IntPtr mogą być również używane do przechowywania uchwytów. Na przykład wystąpienia IntPtr są szeroko używane w klasie System.IO.FileStream do przechowywania uchwytów plików.

Typ IntPtr jest zgodny z CLS, a typ UIntPtr nie. W środowisku uruchomieniowym języka wspólnego używany jest tylko typ IntPtr. Typ UIntPtr jest dostarczany głównie w celu zachowania symetrii architektonicznej z typem IntPtr.

http://msdn.microsoft.com/en-us/library/system.intptr(VS.71).aspx


5

Cóż, to jest strona MSDN, która zajmuje się IntPtr.

Pierwsza linia brzmi:

Typ specyficzny dla platformy, który jest używany do reprezentowania wskaźnika lub uchwytu.

Co do tego, jaki wskaźnik lub uchwyt jest na stronie, stwierdza:

Typ IntPtr może być używany w językach obsługujących wskaźniki oraz jako powszechny sposób odwoływania się do danych między językami, które obsługują i nie obsługują wskaźników.

Obiekty IntPtr mogą być również używane do przechowywania uchwytów. Na przykład wystąpienia IntPtr są szeroko używane w klasie System.IO.FileStream do przechowywania uchwytów plików.

Wskaźnik to odniesienie do obszaru pamięci, w którym znajdują się interesujące Cię dane.

Uchwyt może być identyfikatorem obiektu i jest przekazywany między metodami / klasami, gdy obie strony potrzebują dostępu do tego obiektu.


2

An IntPtrto typ wartości, który jest używany głównie do przechowywania adresów lub uchwytów pamięci. Wskaźnik jest adresem pamięci. Wskaźnik może być wpisany (np. int*) Lub bez typu (np void*.). Uchwyt systemu Windows jest to wartość, która jest zwykle taki sam rozmiar (lub mniejszy) niż adres pamięci i reprezentuje zasobów systemowych (np pliku lub okna).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.