Annuncio pubblicitario
Che tu lo realizzi o no, la stragrande maggioranza dei programmi che hai utilizzato fa uso di puntatori in qualche modo. Forse hai sperimentato a NullPointerException ad un certo punto. Come programmatore, il codice che scrivi utilizzerà molto probabilmente i puntatori, anche se non li hai implementati tu stesso.
Oggi ti mostrerò come funzionano i puntatori, quindi potresti voler dare un'occhiata come funzionano le matrici e le liste Funzionamento di array ed elenchi in PythonLe matrici e gli elenchi sono alcune delle strutture di dati più utili nella programmazione, anche se poche persone le usano al massimo delle loro potenzialità. Leggi di più per un primer di programmazione. Questo articolo sarà più basato sulla teoria del solito, ma attenersi ad esso, i puntatori sono molto complessi!
Codice di compilazione
Prima di scavare nei puntatori, devi capire come viene costruito ed eseguito il codice, forse lo sai già. Questa sezione avrà dichiarazioni abbastanza generali - cose che si applicano al maggioranza di lingue, ma non necessariamente tutte.
Riportiamo le cose all'inizio. Ogni computer usa binario Che cos'è il binario? [Spiegazione della tecnologia]Dato che il binario è così assolutamente fondamentale per l'esistenza dei computer, sembra strano che non abbiamo mai affrontato l'argomento prima, quindi oggi ho pensato di dare una breve panoramica di ciò che il binario ... Leggi di più , una serie di quelli e zeri che compongono la tecnologia moderna come la conosciamo. È estremamente difficile codificare qualsiasi cosa in binario (i file sarebbero molto confusi), in quanto queste sono le istruzioni grezze necessarie al tuo Unità centrale di elaborazione o CPU per funzionare Che cos'è una CPU e cosa fa?Gli acronimi informatici sono confusi. Che cos'è comunque una CPU? E ho bisogno di un processore quad o dual-core? Che ne dici di AMD o Intel? Siamo qui per aiutarti a spiegare la differenza! Leggi di più . Questo è noto come Codice macchina.
Il prossimo passaggio dal codice della macchina è montaggio. Questo è un formato leggibile in qualche modo umano. Mentre è ancora complesso da programmare, è possibile. L'assemblaggio è costituito da una serie di semplici comandi per eseguire attività ed è noto come a basso livello linguaggio di programmazione. È possibile scrivere programmi complessi, ma è difficile esprimere concetti astratti e richiede molta considerazione.
Molti videogiochi e applicazioni ad alte prestazioni hanno una parte della logica scritta in assemblea, poiché alcuni veri aumenti di velocità possono essere trovati se sai cosa stai facendo. Tuttavia, per la stragrande maggioranza dei progetti di programmazione, non è necessario conoscere alcuna assemblea.
Quindi, se il codice macchina è troppo difficile da scrivere e l'assemblaggio è troppo difficile da programmare, con cosa scrivi il codice? Ecco dove alto livello le lingue entrano. Le lingue di alto livello rendono i programmi facili da scrivere. Puoi programmare in qualcosa che assomigli alla tua lingua madre ed è facile esprimere algoritmi complessi. Potresti aver sentito parlare di molte lingue di alto livello (e avrai sicuramente usato un programma scritto in esse):
- DI BASE
- C ++
- blesità
Queste lingue sono molto antiche ora e molte sono state sviluppate nei primi anni '50! Quasi ogni moderno linguaggio di programmazione è un linguaggio di alto livello, inclusi PHP e Python. Ogni giorno vengono inventate più lingue (anche se probabilmente ce ne sono abbastanza ora), ma come funziona esattamente il tuo codice se i computer richiedono il codice macchina?
Ecco dove arriva la compilation. Un compilatore è un programma che converte il tuo codice di alto livello in un modulo che può essere eseguito. Questo potrebbe essere un altro linguaggio di alto livello, ma di solito è assembly. Alcuni linguaggi (come Python o Java) convertono il tuo codice in uno stadio intermedio chiamato bytecode. Ciò dovrà essere nuovamente compilato in un secondo momento, cosa che di solito viene eseguita su richiesta, ad esempio quando il programma viene eseguito. Questo è noto come appena in tempo compilation ed è abbastanza popolare.
Gestione della memoria
Ora che sai come funzionano i linguaggi di programmazione, diamo un'occhiata alla gestione della memoria in linguaggi di alto livello. Per questi esempi, userò pseudo codice - codice scritto non in una lingua specifica, ma utilizzato per mostrare concetti anziché sintassi esatta. Oggi, questo assomiglierà per lo più al C ++ in quanto è il miglior linguaggio di alto livello (secondo me).
Per questa sezione, sarà utile se hai una comprensione di come funziona la RAM Una guida rapida e sporca alla RAM: cosa devi sapereLa RAM è un componente cruciale di ogni computer, ma può essere fonte di confusione. Lo suddividiamo in termini facili da comprendere che capirai. Leggi di più .
La maggior parte delle lingue ha variabili: contenitori che contengono alcuni dati. Devi definire esplicitamente il tipo di dati. Alcuni linguaggi tipicamente dinamici come Python o PHP gestiscono questo per te, ma devono ancora farlo.
Supponi di avere una variabile:
int myNumber;
Questo codice dichiara una variabile chiamata il mio numeroe gli fornisce un tipo di dati di numero intero. Una volta compilato, il computer interpreta questo comando come:
"Trova un po 'di memoria vuota e riserva uno spazio abbastanza grande per memorizzare un numero intero"
Una volta eseguito questo comando, quel bit di memoria non può essere utilizzato da un altro programma. Non contiene ancora alcun dato, ma è riservato per la variabile myNumber.
Ora assegna un valore alla tua variabile:
myNumber = 10;
Per completare questa attività, il tuo computer accede alla posizione di memoria riservata e modifica qualsiasi valore memorizzato lì, a questo nuovo valore.
Ora, va tutto bene, ma come vengono conservate le posizioni di memoria? Se i programmi riservassero tutta la memoria che preferiscono, la RAM si riempirebbe immediatamente, il che farebbe a molto sistema lento.
Per evitare questo potenziale problema, molte lingue implementano a netturbino, utilizzato per distruggere le variabili (e quindi rilasciare le posizioni di memoria riservate) che sono andate fuori portata.
Forse ti starai chiedendo quale sia lo scopo e perché sia così importante. Scope definisce i limiti e la durata delle variabili o di qualsiasi memoria utilizzata da un programma. Una variabile è "al di fuori dell'ambito" quando non è più possibile accedervi da alcun codice (è quando interviene il garbage collector). Ecco un esempio:
function maths () {int firstNumber = 1; } int secondNumber = 2; print (firstNumber + secondNumber); // non funzionerà
Questo esempio non verrà compilato. La variabile firstNumber è all'interno del matematica funzione, quindi questo è lo scopo. Non è possibile accedervi dall'esterno della funzione in cui è stata dichiarata. Questo è un importante concetto di programmazionee la comprensione è fondamentale per lavorare con i puntatori.
Questo modo di gestire la memoria è chiamato pila. È il modo in cui funziona la stragrande maggioranza dei programmi. Non è necessario comprendere i puntatori per usarlo ed è abbastanza ben strutturato. Lo svantaggio dello stack è la velocità. Poiché il computer deve assegnare memoria, tenere traccia delle variabili ed eseguire la garbage collection, c'è un piccolo overhead. Questo va bene per i programmi più piccoli, ma per quanto riguarda le attività ad alte prestazioni o le applicazioni pesanti di dati?
Invio: puntatori.
puntatori
In superficie, i puntatori sembrano semplici. Si riferiscono (indicare) una posizione in memoria. Questo potrebbe non sembrare diverso dalle variabili "normali" nello stack, ma fidati di me, c'è un'enorme differenza. I puntatori sono memorizzati sul mucchio. Questo è l'opposto dello stack: è meno organizzato, ma è molto più veloce.
Vediamo come vengono assegnate le variabili nello stack:
int numberOne = 1; int numberTwo = numberOne;
Questa è sintassi semplice; La variabile numero due contiene il numero uno. Il valore viene copiato durante l'assegnazione da numero uno variabile.
Se volessi ottenere il indirizzo di memoria di una variabile, anziché il suo valore, devi utilizzare il segno e commerciale (&). Questo si chiama il indirizzo di operatore ed è una parte essenziale del toolkit puntatore.
int numberOne = 1; int numberTwo = & numberOne;
Ora il numero due variabile punti in una posizione di memoria, piuttosto che ottenere il numero uno copiato nella sua nuova posizione di memoria. Se dovessi produrre questa variabile, non sarebbe il numero uno (anche se è memorizzato nella posizione della memoria). Produrrà l'ubicazione della memoria (probabilmente qualcosa come 2167, anche se varia a seconda del sistema e della RAM disponibile). Per accedere al valore memorizzato in un puntatore, anziché nella posizione di memoria, è necessario dereference il puntatore. Questo accede direttamente al valore, che sarebbe il numero uno in questo caso. Ecco come si dereference un puntatore:
int numberTwo = * numberOne;
Il operatore di dereference è un asterisco (*).
Questo può essere un concetto difficile da capire, quindi ripassiamolo:
- Il indirizzo di L'operatore (&) memorizza l'indirizzo di memoria.
- Il operatore di dereference (*) accede al valore.
La sintassi cambia leggermente quando si dichiarano i puntatori:
int * myPointer;
Il tipo di dati di int qui si riferisce al tipo di dati del puntatore punti e non il tipo del puntatore stesso.
Ora che sai quali sono i puntatori, puoi fare alcuni trucchi davvero accurati con loro! Quando viene utilizzata la memoria, viene avviato il sistema operativo sequenzialmente. Puoi pensare alla RAM come a buchi di piccione. Molti buchi per conservare qualcosa, solo uno può essere usato alla volta. La differenza qui è, questi buchi di piccione sono tutti numerati. Quando si assegna la memoria, il sistema operativo si avvia dal numero più basso e funziona. Non salterà mai tra numeri casuali.
Quando si lavora con i puntatori, se è stata assegnata una matrice, è possibile passare facilmente all'elemento successivo semplicemente incrementando il puntatore.
Ecco dove diventa interessante. Quando si passano i valori a una funzione (utilizzando le variabili archiviate nello stack), questi valori vengono copiati nella funzione. Se si tratta di grandi variabili, il programma ora le memorizza due volte. Al termine della funzione, potrebbe essere necessario un modo per restituire questi valori. Le funzioni in genere possono restituire solo una cosa, quindi cosa succede se si desidera restituire due, tre o quattro cose?
Se si passa un puntatore alla funzione, viene copiato solo l'indirizzo di memoria (che è minuscolo). Ciò consente alla CPU di lavorare molto! Forse il tuo puntatore punta a un enorme array di immagini - non solo la tua funzione può funzionare esattamente allo stesso modo i dati sono archiviati nella stessa identica posizione di memoria, ma una volta fatto, non è necessario restituire nulla.! Neat
Devi stare molto attento però. I puntatori possono ancora uscire dal campo di applicazione ed essere raccolti dal garbage collector. I valori memorizzati nella memoria, tuttavia, non vengono raccolti. Questa è chiamata perdita di memoria. Non è più possibile accedere ai dati (poiché i puntatori sono stati distrutti), ma sta ancora esaurendo la memoria. Questo è un motivo comune per cui molti programmi si arrestano in modo anomalo e può fallire in modo spettacolare se c'è una grande quantità di dati. Il più delle volte, il tuo sistema operativo ucciderà il tuo programma se hai una grande perdita (usando più RAM di quella del sistema), ma questo non è desiderabile.
I puntatori di debug possono essere un incubo, soprattutto se si lavora con grandi quantità di dati o in loop. I loro svantaggi e difficoltà di comprensione valgono davvero i compromessi che ottieni in termini di prestazioni. Anche se ricorda, potrebbero non essere sempre richiesti.
Questo è tutto per oggi. Spero che tu abbia imparato qualcosa di utile su un argomento complesso. Naturalmente, non abbiamo coperto tutto ciò che c'è da sapere - è un argomento molto complesso. Se sei interessato a saperne di più, lo consiglio vivamente C ++ in 24 ore.
Se questo era un po 'complesso, dai un'occhiata la nostra guida ai linguaggi di programmazione più semplici 6 linguaggi di programmazione più semplici da imparare per i principiantiImparare a programmare significa trovare la lingua giusta tanto quanto il processo di edificazione. Ecco i primi sei linguaggi di programmazione più facili per i principianti. Leggi di più .
Hai imparato come funzionano i puntatori oggi? Hai suggerimenti e trucchi che vuoi condividere con altri programmatori? Salta nei commenti e condividi i tuoi pensieri qui sotto!
Joe è laureato in Informatica presso l'Università di Lincoln, nel Regno Unito. È uno sviluppatore di software professionale e quando non pilota droni o scrive musica, spesso si trova a scattare foto o a produrre video.