Che cos'è esattamente un IntPtr?


171

Usando IntelliSense e guardando il codice di altre persone, mi sono imbattuto in questo IntPtrtipo; ogni volta che è necessario usarlo ho semplicemente messo nullo IntPtr.Zerotrovato la maggior parte delle funzioni. Che cos'è esattamente e quando / perché viene utilizzato?

Risposte:


158

È un "numero intero nativo (specifico della piattaforma)". È rappresentato internamente come void*ma esposto come un numero intero. È possibile utilizzarlo ogni volta che è necessario archiviare un puntatore non gestito e non si desidera utilizzare il unsafecodice. IntPtr.Zeroè effettivamente NULL(un puntatore nullo).


53
Un puntatore è qualcosa che punta a un indirizzo in memoria. Nelle lingue gestite hai riferimenti (l'indirizzo può spostarsi) mentre nelle lingue non gestite hai puntatori (l'indirizzo è fisso)
Colin Mackay,

93
In generale (attraverso i linguaggi di programmazione), un puntatore è un numero che rappresenta una posizione fisica nella memoria. Un puntatore null è (quasi sempre) uno che punta a 0 ed è ampiamente riconosciuto come "non punta a nulla". Poiché i sistemi hanno quantità diverse di memoria supportata, non sempre è necessario lo stesso numero di byte per contenere quel numero, quindi chiamiamo un "numero intero nativo" in grado di contenere un puntatore su un particolare sistema.
Sam Harwell,

5
+1 per quel commento @ 280Z28, questa è la spiegazione più succinta dei puntatori che abbia mai visto.
BlueRaja - Danny Pflughoeft,

9
Si chiama IntPtr, perché per usarlo dal codice nativo non gestito, C / C ++, dovrai usare il tipo analogico: intptr_t. IntPtr di C # si associa esattamente a intptr_t di C / C ++. Ed è probabilmente implementato come intptr_t. In C / C ++, il tipo intptr_t ha la stessa dimensione del tipo void *.
Петър Петров

2
@Trap "Un tipo specifico della piattaforma utilizzato per rappresentare un puntatore o un handle." Non un "numero intero specifico della piattaforma", un "modo specifico della piattaforma per rappresentare un puntatore o un handle". IntPtrha perfettamente senso, perché è un numero intero (come nell'intero matematico) e un puntatore (come nel puntatore). La Intparte ha poco a che fare con il tipo int: è un concetto, non un tipo specifico. Sebbene sia giusto, l'unica cosa che le specifiche CLI dicono a riguardo è che si tratta effettivamente di un "numero intero, dimensione nativa". Oh bene :)
Luaan,

69

È un tipo di valore abbastanza grande da memorizzare un indirizzo di memoria utilizzato nel codice nativo o non sicuro, ma non direttamente utilizzabile come indirizzo di memoria nel codice gestito sicuro.

È possibile utilizzare IntPtr.Sizeper scoprire se si sta eseguendo un processo a 32 o 64 bit, poiché saranno rispettivamente 4 o 8 byte.


16
Un buon punto per rilevare la dimensione dello spazio degli indirizzi per il processo.
Noldorin,

@Noldorin Vero, ma non necessariamente affidabile. In passato, ci sono state molte architetture che avevano più tipi di puntatori e su Windows IntPtrè anche usato per rappresentare handle che sono a 32 bit indipendentemente dall'architettura (anche se Sizein questo caso dice ancora 8). La specifica CLI nota solo che è un numero intero di "dimensione nativa", ma in realtà non dice molto.
Luaan,

@Luaan che non cambia nulla nella mia risposta, vero? IntPtr è chiamato (e viene utilizzato in tutta l'origine CLR) come un valore abbastanza grande da contenere un indirizzo di memoria. Naturalmente può contenere valori più piccoli. Alcune architetture hanno più tipi di puntatori, ma devono avere uno che sia il più grande dell'insieme.
Daniel Earwicker,

@DanielEarwicker Bene, non è un problema con nessuna implementazione corrente di .NET, per quanto ne so. Tuttavia, il problema (storico) non riguarda solo le dimensioni: i vari puntatori potrebbero essere del tutto incompatibili. In un esempio più vicino a oggi, PAE userebbe indirizzi a 64 bit anche se la "dimensione del puntatore nativo" era ancora a 32 bit. Risale all'argomento "cosa significa" sistema a 32 bit ", in realtà?" Ora abbiamo registri numerici a 256 e 512 bit, ma lo chiamiamo ancora a 64 bit. Anche se in realtà non è possibile indirizzare 64 bit di memoria fisica con i "puntatori" a 64 bit. È un disastro.
Luaan,

1
In parte è complicato, ma IntPtr è ancora "un tipo di valore abbastanza grande da memorizzare un indirizzo di memoria". Non è grande come il più grande registro hardware, per fare uno dei tuoi esempi. Non è per quello che serve. È abbastanza grande da rappresentare un indirizzo di memoria. Poi ci sono cose come questa: stackoverflow.com/questions/12006854/… dove abbiamo usato la parola "puntatore" per qualcosa di diverso da un indirizzo di memoria - motivo per cui ho specificamente detto "indirizzo di memoria".
Daniel Earwicker,

48

Ecco un esempio:

Sto scrivendo un programma C # che si interfaccia con una telecamera ad alta velocità. La fotocamera ha il suo driver che acquisisce le immagini e le carica automaticamente nella memoria del computer.

Quindi quando sono pronto a portare l'ultima immagine nel mio programma con cui lavorare, il driver della fotocamera mi fornisce un IntPtr su dove l'immagine è GIÀ memorizzata nella memoria fisica, quindi non devo perdere tempo / risorse per crearne un'altra blocco di memoria per memorizzare un'immagine che è già in memoria. IntPtr mi mostra solo dove si trova già l'immagine.


7
Quindi IntPtr simple ti consente di utilizzare un puntatore non gestito (come quello utilizzato nel driver della fotocamera) nel codice gestito?
Callum Rogers,

5
Sì. Bene, in questo caso, molto probabilmente il driver della fotocamera utilizza driver non gestiti sotto il cofano, ma per funzionare correttamente nel mondo solo gestito, IntPtr mi consente di lavorare con i dati in modo sicuro.
bufferz

3
Quindi perché non restituirti semplicemente uno stream (array di byte)? Perché non è sicuro o non è possibile restituire il valore?
The Muffin Man

35

Un'interpretazione diretta

Un IntPtr è un numero intero che ha le stesse dimensioni di un puntatore .

È possibile utilizzare IntPtr per memorizzare un valore di puntatore in un tipo non puntatore. Questa funzionalità è importante in .NET poiché l'uso dei puntatori è altamente soggetto a errori e quindi illegale nella maggior parte dei contesti. Consentendo al valore del puntatore di essere archiviato in un tipo di dati "sicuro", l'impianto idraulico tra segmenti di codice non sicuri può essere implementato in un codice di alto livello più sicuro - o anche in un linguaggio .NET che non supporta direttamente i puntatori.

Le dimensioni di IntPtr sono specifiche della piattaforma, ma raramente questi dettagli devono essere considerati, poiché il sistema utilizzerà automaticamente le dimensioni corrette.

Il nome "IntPtr" è confuso - qualcosa di simile Handleavrebbe potuto essere più appropriato. La mia ipotesi iniziale era che "IntPtr" fosse un puntatore a un numero intero. La documentazione MSDN di IntPtr entra nei dettagli in qualche modo enigmatico senza mai fornire molte informazioni sul significato del nome.

Una prospettiva alternativa

An IntPtrè un puntatore con due limitazioni:

  1. Non può essere direttamente verificato
  2. Non conosce il tipo di dati a cui punta.

In altre parole, un IntPtrè proprio come un void*- ma con la funzione extra che può (ma non dovrebbe) essere usato per l'aritmetica del puntatore di base.

Per dereferenziare un IntPtr, puoi lanciarlo su un puntatore vero (un'operazione che può essere eseguita solo in contesti "non sicuri") oppure puoi passarlo a una routine di supporto come quella fornita dalla InteropServices.Marshalclasse. L'uso della Marshalclasse dà l'illusione della sicurezza poiché non richiede di essere in un contesto esplicito "non sicuro". Tuttavia, non rimuove il rischio di crash che è inerente all'uso dei puntatori.


2
Esistono alcuni precedenti per il nome "intptr" dallo standard C99 del linguaggio di programmazione "C". linux.die.net/man/3/intptr_t .
Brent Bradburn,

17

Che cos'è un puntatore?

In tutte le lingue, un puntatore è un tipo di variabile che memorizza un indirizzo di memoria e puoi chiedere loro di dirti l' indirizzo a cui stanno puntando o il valore all'indirizzo a cui stanno puntando.

Un puntatore può essere pensato come una sorta di segno di libro. Tranne, invece di essere utilizzato per saltare rapidamente a una pagina di un libro, viene utilizzato un puntatore per tenere traccia o mappare i blocchi di memoria.

Immagina la memoria del tuo programma esattamente come un grande array di 65535 byte.

I puntatori indicano obbedientemente

I puntatori ricordano ciascuno un indirizzo di memoria e quindi puntano ciascuno a un singolo indirizzo in memoria.

Come gruppo, i puntatori ricordano e richiamano gli indirizzi di memoria, obbedendo a ogni comando fino alla nausea.

Sei il loro re.

Puntatori in C #

Nello specifico in C #, un puntatore è una variabile intera che memorizza un indirizzo di memoria compreso tra 0 e 65534.

Anche specifici per C #, i puntatori sono di tipo int e quindi firmati.

Tuttavia, non è possibile utilizzare indirizzi con numero negativo, né è possibile accedere a un indirizzo superiore a 65534. Qualsiasi tentativo in tal senso genererà un'eccezione System.AccessViolationException.

Un puntatore chiamato MyPointer è dichiarato così:

int * MyPointer;

Un puntatore in C # è un int, ma gli indirizzi di memoria in C # iniziano da 0 e si estendono fino a 65534.

Le cose appuntite dovrebbero essere gestite con particolare attenzione

La parola non sicuro è destinato a spaventare voi, e per una buona ragione: I puntatori sono cose appuntite, e le cose appuntite ad esempio spade, asce, puntatori, ecc dovrebbero essere trattati con particolare attenzione.

I puntatori danno al programmatore il controllo stretto di un sistema. Pertanto è probabile che gli errori commessi abbiano conseguenze più serie.

Per utilizzare i puntatori, è necessario abilitare il codice non sicuro nelle proprietà del programma e i puntatori devono essere utilizzati esclusivamente in metodi o blocchi contrassegnati come non sicuri.

Esempio di un blocco non sicuro

unsafe
{
    // Place code carefully and responsibly here.

}

Come usare i puntatori

Quando le variabili o gli oggetti vengono dichiarati o istanziati, vengono memorizzati.

  • Dichiarare un puntatore usando il prefisso simbolo *.

int *MyPointer;

  • Per ottenere l'indirizzo di una variabile, utilizzare il prefisso & symbol.

MyPointer = &MyVariable;

Una volta assegnato un indirizzo a un puntatore, si applica quanto segue:

  • Senza * prefisso per fare riferimento all'indirizzo di memoria indicato come int.

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

  • Con il prefisso * per ottenere il valore memorizzato all'indirizzo di memoria indicato.

"MyPointer is pointing at " + *MyPointer;

Poiché un puntatore è una variabile che contiene un indirizzo di memoria, questo indirizzo di memoria può essere memorizzato in una variabile puntatore.

Esempio di puntatori usati con cura e responsabilità

    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.

    }

Si noti che il tipo di puntatore è un int. Questo perché C # interpreta gli indirizzi di memoria come numeri interi (int).

Perché è int invece di uint?

Non c'è una buona ragione.

Perché usare i puntatori?

I puntatori sono molto divertenti. Con gran parte del computer controllato dalla memoria, i puntatori consentono a un programmatore un maggiore controllo della memoria del proprio programma.

Monitoraggio della memoria.

Utilizzare i puntatori per leggere i blocchi di memoria e monitorare come i valori puntati cambiano nel tempo.

Modifica questi valori in modo responsabile e tieni traccia di come le modifiche influiscono sul tuo computer.


2
65534sembra molto sbagliato come intervallo del puntatore. Dovresti fornire un riferimento.
Brent Bradburn,

1
Ho votato a favore perché è un ottimo articolo che spiega i puntatori. Vorrei vedere alcuni riferimenti alle specifiche che hai citato (come commentato sopra). Però; questa risposta non ha nulla a che fare con la domanda. La domanda riguardava System.IntPtr: Vorrei vedere la risposta aggiornata alla fine spiegando anche cosa System.IntPtre come si riferisce ai puntatori non sicuri in C # per favore.
Michael Puckett II

7

MSDN ci dice:

Il tipo IntPtr è progettato per essere un numero intero le cui dimensioni sono specifiche della piattaforma. Vale a dire, un'istanza di questo tipo dovrebbe essere a 32 bit su hardware e sistemi operativi a 32 bit e 64 bit su hardware e sistemi operativi a 64 bit.

Il tipo IntPtr può essere utilizzato da lingue che supportano i puntatori e come mezzo comune per fare riferimento ai dati tra lingue che supportano e non supportano i puntatori.

Gli oggetti IntPtr possono anche essere utilizzati per contenere handle. Ad esempio, le istanze di IntPtr sono ampiamente utilizzate nella classe System.IO.FileStream per contenere handle di file.

Il tipo IntPtr è conforme a CLS, mentre il tipo UIntPtr non lo è. Nel runtime di linguaggio comune viene utilizzato solo il tipo IntPtr. Il tipo UIntPtr viene fornito principalmente per mantenere la simmetria architettonica con il tipo IntPtr.

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


5

Bene, questa è la pagina MSDN che tratta IntPtr.

La prima riga recita:

Un tipo specifico della piattaforma utilizzato per rappresentare un puntatore o un handle.

Per quanto riguarda quale puntatore o handle è la pagina continua a dichiarare:

Il tipo IntPtr può essere utilizzato da lingue che supportano i puntatori e come mezzo comune per fare riferimento ai dati tra lingue che supportano e non supportano i puntatori.

Gli oggetti IntPtr possono anche essere utilizzati per contenere handle. Ad esempio, le istanze di IntPtr sono ampiamente utilizzate nella classe System.IO.FileStream per contenere handle di file.

Un puntatore è un riferimento a un'area di memoria che contiene alcuni dati a cui sei interessato.

Un handle può essere un identificatore per un oggetto e viene passato tra metodi / classi quando entrambe le parti devono accedere a quell'oggetto.


2

An IntPtrè un tipo di valore utilizzato principalmente per contenere indirizzi di memoria o handle. Un puntatore è un indirizzo di memoria. Un puntatore può essere digitato (ad es. int*) O non tipizzato (ad es void*.). Un handle di Windows è un valore di solito delle stesse dimensioni (o inferiore) di un indirizzo di memoria e rappresenta una risorsa di sistema (come un file o una finestra).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.