Qual è una bella spiegazione per i puntatori? [chiuso]


72

Nei tuoi studi (da solo o per una lezione) hai avuto un momento "ah ah" in cui finalmente hai capito davvero i suggerimenti? Hai una spiegazione che usi per i programmatori principianti che sembra particolarmente efficace?

Ad esempio, quando i principianti primi puntatori incontro in C, potrebbero semplicemente aggiungere &s e *s fino a quando non compila (come io stesso una volta fatto). Forse è stata una foto, o un esempio davvero ben motivato, che ha fatto "puntare" i puntatori per te o il tuo studente. Cos'è stato e cosa hai provato prima che non sembrasse funzionare? Ci sono stati prerequisiti di argomenti (ad es. Strutture o matrici)?

In altre parole, ciò che era necessario per comprendere il significato di &s e *, quando li si potrebbe usare con fiducia? L'apprendimento della sintassi e della terminologia o dei casi d'uso non è sufficiente, a un certo punto l'idea deve essere interiorizzata.


Aggiornamento: mi piacciono molto le risposte finora; per favore, continua a venire. Ci sono molte grandi prospettive qui, ma penso che molte siano buone spiegazioni / slogan per noi stessi dopo aver interiorizzato il concetto. Sto cercando i contesti e le circostanze dettagliati quando ti è venuto in mente.

Per esempio:

In C. ho capito sinteticamente solo i puntatori in C. Ho sentito due dei miei amici che spiegavano i puntatori a un altro amico, che mi chiedeva perché a structfosse passato un puntatore. Il primo amico ha parlato di come doveva essere referenziato e modificato, ma era solo un breve commento dell'altro amico in cui mi ha colpito: "È anche più efficiente". Passare 4 byte invece di 16 byte è stato lo spostamento concettuale finale di cui avevo bisogno.


16
Metodo "Shotgun": lancia * s ovunque fino a quando funziona.
Michael K,

15
@Michael: Sì, buona fortuna con quello.
Robert Harvey,

8
Questa domanda sorge poco dopo all'inizio di ogni semestre su SO. L'ultima incarnazione è qui: stackoverflow.com/questions/4118647/…
Tim Post

10
Qual è una bella spiegazione per i puntatori? Brian Kernhigan e Dennis Ritchie odiano gli ingegneri del software.
Adam Crossland,

3
Ha "cliccato" per me dopo aver trascorso un po 'di tempo nel codice del debugger il codice macchina single stepping. Sì, avevo familiarità con le basi dell'architettura del computer - ho imparato l'assemblaggio su Commodore 64 e x86 ASM è stato piuttosto semplice. Quindi conoscevo già il concetto di "indirizzo", semplicemente non capivo come si associasse ai costrutti sintattici di C.
zvrba,

Risposte:


25

Diagramma della memoria come griglia

Di solito quello che faccio è rappresentare la memoria come una "griglia", in modo da poter creare indirizzi, evidenziare diversi spazi di memoria e scrivere nei valori delle celle (o anche ulteriormente, le loro rappresentazioni binarie) e collegare i puntatori in memoria ai valori che essi indicare. (E poi menziona ancora che è una semplificazione).

Di solito è un momento "ohhhh" per la maggior parte dei miei studenti.

Simbolo Giocoleria

Quindi quando si tratta di smettere di dimenticare come usare & e *, è molto semplice: presentarlo nello stesso modo in cui eseguono calcoli matematici o fisici. Se si divide una distanza in km per un'ora in ora, si ottiene una velocità in km / h. Cosa c'è dentro deve essere fuori. Semplice.

printf to the Rescue

Fare solo alcuni esempi di base che rappresentano visivamente ciò che hai spiegato con questi li consolerà in ciò che pensano di aver capito, o darà loro l'opportunità di dire "ah, non capisco questo".

Sii ampio

Copri i puntatori per tipi semplici e assicurati che comprendano la differenza tra indirizzamento e dimensione di un tipo di dati, quindi strutture, quindi array e livelli multipli.

Quindi avviare l'aritmetica del puntatore.


Addendum: ricorsione

Di solito spiego la ricorsione in modo simile, usando una rappresentazione visiva. Invitali a stampare l'alfabeto utilizzando una funzione predefinita che scrive un singolo carattere, quindi chiedi loro di stamparlo in ordine inverso cambiando solo due righe.

Di solito c'è un "what the ...?" momento e quando aggiungi solo un altro parametro al tuo printf per stampare valori numerici e rientrare i passaggi, diventa un sospiro di sollievo.


Alternative: il modello Play-Doh e le tazze d'acqua

In realtà ho avuto alcuni colleghi in un'università che hanno mostrato agli studenti un video che spiega i puntatori e gli accessi alla memoria usando play-doh paste. È stato incredibilmente intelligente e ben fatto, anche se non ho mai usato quella tecnica da solo, ad eccezione di studenti molto giovani interessati a comprendere la programmazione (ma di solito quelli che non li avrei condotti verso una lingua usando puntatori troppo presto). Fondamentalmente usando minuscole sfere di play-doh che è possibile collegare ad altre sfere più grandi di play-doh che rappresentano gli spazi di memoria e che è possibile combinare insieme per collegarle (come in una struttura dati collegata) o unire insieme (come in una contigua spazio di memoria). L'uso di colori diversi per gli spazi di memoria indicati e anche i puntatori aiutano. Ma penso ancora che la cosa Memory-as-a-Grid funzioni meglio, come puoi chiaramente dimostrare che il puntamento è davvero una questione di "indirizzamento", come su una "mappa / griglia". Considerando che la modalità Play-doh li confonde ancora nel pensare che le cose si "tocchino" davvero nella memoria.

Anche la tazza d'acqua è stata usata direttamente da un collega, ma non so se sia venuta fuori. È stato un approccio interessante, ma ho notato che molti studenti sono rimasti perplessi dalla spiegazione. Qualcosa di simile alla tecnica della tazza di caffè DevSolo . Ma penso che sia in realtà fuorviante mentre fai confondere gli studenti con contenitori, strutture dati, puntatori e array. Presumo che potrebbe essere un approccio interessante per spiegare gli array all'inizio, ma non mi atterrei molto a lungo.


Wow, grazie per la generosità. Sono contento che la risposta sia stata apprezzata.
haylem,

Disegnalo: | sembra un buon insegnamento!
Spaventa l'

80

Qualcuno molto più saggio di quanto dicessi una volta:

La suora Wu Jincang chiese al sesto Patriach Huineng: "Ho studiato il sutra Mahaparinirvana per molti anni, ma ci sono molte aree che non capisco del tutto. Per favore, illuminami."

Il patriach rispose: "Sono analfabeta. Per favore, leggimi i personaggi e forse potrò spiegarne il significato."

Disse la suora, "Non puoi nemmeno riconoscere i personaggi. Come riesci a capirne il significato?"

"La verità non ha nulla a che fare con le parole. La verità può essere paragonata alla luna luminosa nel cielo. Le parole, in questo caso, possono essere paragonate a un dito. Il dito può indicare la posizione della luna. Tuttavia, il dito non è il luna. Per guardare la luna, è necessario guardare oltre il dito, giusto? "


13
+1 per dereferenziare la luna. +1 per il koan, se potessi.
Tim Post

14
Qual è il suono del voto di un dito?
Adam Crossland,

3
@Adam dipende dal fatto che tu stia utilizzando un mouse o un trackpad.
Daniel Joseph,

7
@Frank È stata una GRANDE citazione. Penso comunque che non aiuterà a cogliere l'idea. Specialmente per qualcuno che non conosce i puntatori.
Gulshan,

11
Questa è una bella storia ... ma sospetto che le persone stiano votando per questo, piuttosto che perché spiegano bene i suggerimenti.
Kyralessa,

46

Ho scoperto che i diagrammi sono stati molto utili. Esempio:

diagramma puntatore


Questo tipo di diagramma mi ha aiutato a vedere che i puntatori erano la propria variabile, ma conteneva un valore che era la posizione di un altro oggetto, ovvero array o stringa. Inoltre, una volta fatto a matita, potrei usarlo per tracciare il mio programma su carta o su una lavagna / lavagna.


4
Il valore all'interno di "pointer" dovrebbe essere 4 o gli indici di array (o indirizzi di memoria) dovrebbero iniziare da 0. Altrimenti, come può un puntatore che contiene il valore 3 puntare alla posizione 4?
MAK

++ Questo è esattamente come stavo per spiegarlo. Ho iniziato a Fortran (nel bene o nel male!). A Fortran, allora, abbiamo usato array paralleli invece di array di strutture (niente di nuovo ). Se un array conteneva un indice di "un'altra riga" negli array, lo chiamavamo un "puntatore", e questo è stato eccitante. Abbiamo creato liste collegate, alberi, tu lo chiami.
Mike Dunlavey,

2
Trovo i puntatori meglio spiegati usando le immagini che indicano cosa è memorizzato dove e le frecce per indicare dove puntano i puntatori.
gablin,

32

Quando ho "appreso" per la prima volta i puntatori, sono stato un po 'coinvolto. La mia università aveva preso la decisione molto tempo prima che mi iscrivessi per centrare il curriculum attorno a Java, quindi quando il mio professore di Strutture di dati tenne una lezione su C e ci chiese di implementare un elenco XOR con puntatori mi sentivo come se stessi entrando in qualcosa molto sopra la mia testa.

Ho capito la definizione:

Un puntatore è una variabile che contiene un indirizzo di una variabile

Ma non ho ancora capito gran parte del concetto. Guardando indietro, penso che fosse incentrato su tre cose:

  1. Che cosa esattamente è una locazione di memoria? (All'epoca non frequentavo un corso di organizzazione informatica)

  2. La sintassi scomoda (So um ... perché esattamente è definita come "int * ip" ma poi mi riferisco alla variabile come "ip"?)

  3. In che modo è esattamente vantaggioso memorizzare l'indirizzo di una variabile piuttosto che utilizzarla?

Non è stato fino a quando ho acquistato il libro K&R e completato tutti i problemi che ho avuto davvero una comprensione per i puntatori. Oltre al fatto che avevo da tempo completato la lezione di Computer Organization (che penso dovrebbe essere richiesta prima di imparare C), parte di essa ha avuto a che fare con il fatto che ho capito che i puntatori possono essere usati per funzioni, array, strutture ... fare cose utili e non semplicemente come memoria per indirizzi di variabili ordinarie.

Ma il mio momento "aha" è stato di gran lunga il modo in cui K&R ha spiegato l'imbarazzante sintassi della definizione di un semplice puntatore. Ho preso appunti in tutto il libro (in cui riformulo i punti sollevati dal libro con le mie parole per migliorare la mia comprensione), e questo è quello relativo a quello:

Un puntatore è una variabile che contiene un indirizzo per una variabile. Un puntatore è sia definito sia dereferenziato (restituendo il valore memorizzato nella posizione di memoria a cui punta) con l'operatore '*'; l'espressione è mnemonica.

Ex.: 
   int a;      /* Variable 'a' is an integer */

   int *ip;   /* Variable ip is a pointer and dereferencing it gives an integer.
                 In other words, the expression *ip is an int, so ip is a pointer
                 to an int */

Avevo sempre pensato di non riuscire a cogliere appieno i concetti più avanzati di puntatori fino a quando non avevo qualcosa di così elementare radicato nella mia testa. Mi aveva infastidito all'infinito dopo quell'incarico (che non avevo fatto troppo bene, tra l'altro;)) perché "* ip" non esisteva subito dopo che (pensavo di) definito "* ip". La padronanza di questo è essenziale per concetti più avanzati che coinvolgono puntatori, come puntatori a funzioni e definizioni più complicate come questa:

char (*(x())[])()

Tutto sommato, penso che il concetto di puntatori richieda:

  1. Una comprensione di base di come la memoria è disposta in un computer (e che cos'è la memoria).

  2. Conoscenza di quanto possano essere potenti i puntatori (utilizzo nel mondo reale, non solo un altro concetto astratto che apprendono per motivi di apprendimento).

  3. Decifrare i geroglifici colloquialmente noti come "una definizione di un puntatore"

Quindi, tutto sommato, penso che dovrebbero esserci almeno 3 momenti "aha" quando si imparano i puntatori. Sono ancora uno studente, quindi ho pensato che avresti apprezzato il punto di vista di qualcuno che è ancora (relativamente) fresco di apprendimento del concetto.


+1 per il 3 "Ah ah!" monents. Puntatori di Groking sono disponibili in livelli. Ho "capito" i punti per molto tempo prima di rendermi conto di aver compreso solo la metà dei puntatori ai puntatori :)
Binary Worrier

Penso che tu abbia perfettamente ragione in quanto una parte molto ampia di "non capire i puntatori" sta effettivamente inciampando nella strana sintassi C. [Pensa: mi chiedo quanto sarebbe difficile lamentarsi con questo tipo di sintassi: PointerTo<int> a]
Benjol,

D'accordo, @Benjol. Sono sorpreso che la sintassi int* ipnon sia più comune. Alla fine ho iniziato a capire i puntatori dopo aver visto il codice formattato in questo modo. Quindi potrei leggerlo come "ip è di tipo int pointer".
Jacob,

19

Il puntatore è un po 'come le scorciatoie dell'applicazione sul desktop. Elimina il collegamento e l'obiettivo esiste ancora. Avvia il collegamento e il target viene avviato.

Spiego sempre il funzionamento di questo semplicemente creando un file txt sul mio desktop e due scorciatoie per il file. Dopo aver copiato ed eliminato le scorciatoie, puoi vedere le persone capire l'idea dietro 'riferimenti'.

Una volta che il gruppo capisce le basi dietro le scorciatoie, puoi iniziare a spiegare i puntatori come preferisci. Probabilmente lo capiranno abbastanza facilmente.


Questa non è una cattiva analogia. Solo alcuni punti da aggiungere per chiunque prenda questo letteralmente un "hard-link" indicherà sempre il suo obiettivo anche se cambia directory, un puntatore punterà alla memoria originale anche se l'oggetto è stato trasferito. Un "hard-link" ti dirà quando la destinazione non esiste più, un puntatore del puntatore penzolante dovrebbe essere impostato su NULL.
snmcdonald,

Un collegamento reale è come un puntatore intelligente contato di riferimento
jk.

1
+1. Una delle migliori analogie pratiche per spiegare i puntatori. Sono sicuro che gli studenti otterrebbero immediatamente l'idea in quanto questa funzionalità è la più utilizzata da tutti.
Karthik Sreenivasan,

13

8-10 anni fa, ho insegnato un corso introduttivo "C" in un college della comunità. Questo è sempre stato un argomento divertente da esplorare. Ciò che sembrava funzionare meglio, e questo dopo aver discusso un paio di volte con un collega, era usare una tazza di caffè e la mano.

Ho usato l'analogia di una tazza di caffè (o fila di esse per array) come variabile (può contenere qualcosa). Ho quindi usato la mia mano, che potrebbe anche contenere qualcosa o, estendendo il mio indice per "indicare" una tazza di caffè.

Una mano stretta era nulla, un dito puntato sulla mia testa (come una finta pistola) era un puntatore penzolante.

Quindi, con alcune dimostrazioni e viaggi nel debugger, ha fatto clic con la maggior parte.


3
Mi hai già confuso.
kirk.burleson,

1
@Kirk, come mai? fammi vedere se riesco a chiarirlo.
DevSolo

4
++ Non c'è niente come insegnare a farti pensare a queste cose.
Mike Dunlavey,

Uno dei miei docenti all'università ha fatto una cosa simile, usando gli studenti (uno dei quali era "Niel", per ulteriori risate), invece della tazza di caffè. Indicare con la mano il pubblico in segno di saluto era un puntatore penzolante, perché poteva indicare qualsiasi cosa.
Kaz Dragon,

2
Un puntatore penzolante è più come indicare Neil e poi costringerlo a lasciare la sedia senza cambiare la posizione in cui stai puntando ... quindi provare a seguire quel puntatore e interpretare ciò che trovi lì come Neil, indipendentemente da chi è finito lì.
Jon Purdy,

10

Mi preoccupo molto quando sento la domanda "come hai fatto a capire i puntatori". Ho sempre pensato che il concetto fosse incredibilmente semplice e un'evoluzione logica delle lingue che fornisse un grande potere ai programmatori.

La cosa che mi preoccupa è che non ho mai avuto difficoltà a comprendere il concetto di puntatori. Quindi, quando qui "quando l'hai finalmente capito" ancora e ancora, inizi a pensare:

Lo capisco davvero? Forse non l'ho mai fatto?

Forse il motivo per cui il concetto sembra essere difficile, è perché continuiamo a dire a tutti coloro che devono ancora incontrarli che i puntatori sono così difficili, e qui ci sono cento modi in cui puoi imparare?

Basta lanciare quell'idea là fuori, ovviamente personalmente adoro un buon diagramma e Steve Gibson fa sempre un lavoro fantastico nel spiegare qualcosa !


Interessante ... quale lingua hai usato in quel momento? Penso che la sintassi C inciampi in molte persone. Se conoscessi prima l'assemblea, vedrei che questo è uno dei motivi. [A proposito, voglio votare questo, ma ho raggiunto il mio limite per oggi.]
Macneil,

È interessante notare che non sapevo da tempo puntatori "pericolosi", lavorando in Python (e Java all'università). Poi ho preso un modulo di "metodi orientati agli oggetti" che utilizzava C ++ come linguaggio di scelta e mi sono immerso direttamente lì.
Marcus Whybrow,

Inoltre penso che avere una comprensione di come un programma sia rappresentato in memoria può sicuramente aiutare, ma penso che preferirei dire che capire perché i puntatori fossero un'utile aggiunta a una lingua, è l'angolazione più efficace, dal momento che stai subendo lo stesso però processo come i progettisti originali.
Marcus Whybrow,

+1 Non ho idea del perché la gente pensi che i puntatori siano difficili o misteriosi.
ggambett,

Sì, i puntatori sono sempre stati così ovviamente semplici e utili che non riesco a immaginare di pensare che ci sia qualcosa di "difficile" in loro. Lo odio quando un insegnante di qualsiasi corso dice "ora, questo è un po 'complicato" perché è così sovversivo. Basta insegnare quella dannata roba e lasciare che gli studenti decidano da soli se pensano che sia difficile.
Jon Purdy,

7

Non ho mai avuto grossi problemi con i puntatori in C, ma ciò potrebbe essere dovuto al fatto che prima dovevo imparare l'assemblatore. In realtà è stato un sollievo non dover più gestire gli indirizzi da solo. Quindi forse la risposta (supponendo che questo sia qualcosa che stai insegnando) è quella di fornire agli studenti un linguaggio di assemblaggio emulato con cui lavorare. Essi potranno capirlo nel processo di scrivere qualcosa di più sofisticato di "Ciao, mondo".


+1 per adattare un'astrazione più primitiva, piuttosto che cercare di usare un'analogia.
Tipo anonimo

Nel mio liceo, un'unità di Computer Studies era dedicata allo studio di un computer a scatola di fiammiferi , che può essere insegnato dalla lavagna senza mai toccare o coinvolgere un vero computer.
rwong

@Larry Coleman, Assembly !!!!!!!!!!! Voglio provarlo prima di C. Dimmi da dove iniziare l'apprendimento. Ebook gratuiti, PDF gratuiti? IDE? Per favore!!!!!!!!!!!!!!!!!!!
Spacez Ly Wang,

7

Un puntatore è una variabile il cui valore è l'indirizzo di memoria di un'altra variabile.


Elegante, perché renderlo più complicato.
Bjarke Freund-Hansen,

Se i puntatori non sembrano immediatamente intuitivi e utili, sono stati spiegati in modo errato.
Jon Purdy,

6

Come ho davvero imparato a conoscere i puntatori? Scrivendo un semplice compilatore matricole anno del college.

Come spiegare i suggerimenti nei termini dei laici? Mi piace l'analogia (datata?) Di un catalogo di una biblioteca memorizzato su schede. Ogni carta ("puntatore") contiene informazioni su dove si trova un libro ("dati"), ma in realtà non contiene il libro stesso. Se modifichi la carta ("aritmetica del puntatore"), tutto ciò che fa è cambiare il libro a cui punta e non ha alcun impatto sul libro stesso - fai solo attenzione a non rovinare l'indirizzo o potresti indicare un libro inesistente o anche la libreria sbagliata. Tuttavia, se segui l '"indirizzo" sulla scheda e vai nella parte corretta della libreria ("dereference the pointer"), puoi vedere / modificare il libro stesso.


+1 per la memoria del mio corso CS in cui ho avuto esattamente questa spiegazione
Gary Rowe,

È buono in un certo senso, ma in qualche modo fuorviante, poiché le carte in realtà non dicono dove si trova il libro. È ancora necessario eseguire un algoritmo di ricerca orientato alle sneaker. Forse questo modella meglio un database - facendo riferimento a un oggetto memorizzato da una chiave (numero di chiamata).
DarenW,

+1 L'analogia mantiene lo stesso concetto di quello spiegato da Barfieldmv ma con un approccio alla vita reale.
Karthik Sreenivasan,

5

Presumo che qualcuno imparerà i puntatori sa quali sono le variabili normali e come funzionano in C. Ora, proviamo a definire i puntatori con alcuni dei suoi attributi-

  • Sono anche variabili, ma di diversa natura. Supponiamo che ci siano due livelli nello spazio variabile. Le normali variabili di diverso tipo risiedono nel livello superiore e i puntatori nel livello inferiore. Come questa figura

    testo alternativo

  • Come suggerisce il nome "Puntatore", i puntatori possono PUNTARE a qualcosa. Come il nostro dito può puntare su qualche oggetto. Quali sono le cose che indicano? Queste sono le variabili normali. In breve, "I puntatori puntano a variabili normali".

  • Come le normali variabili, anche i puntatori sono dello stesso numero di tipi come int, char o float. E un puntatore di un tipo specifico può puntare solo allo stesso tipo di variabili.
  • Un puntatore può puntare a una variabile e successivamente lo stesso puntatore può puntare a un'altra variabile. Solo il tipo dovrebbe essere lo stesso. Pertanto, l'associazione del puntatore a una variabile non è permanente e può essere modificata.
  • Quindi, come viene dichiarato un puntatore? Quasi come le normali variabili. Devi precedere il nome con un asterisco ( *). Piace-

    int *pointer;
    
  • Quindi, come viene associato un puntatore a una variabile? Utilizzando l' &operatore prima della variabile come questa affermazione-

    pointer = &variable;
    
  • Come viene utilizzato un puntatore indicando una variabile? Questo viene fatto anche precedendo il nome con un asterisco ( *). Quindi può essere utilizzato al posto della variabile che punta ora-

    *pointer = var1 + var2;
    

    invece di

    variable = var1 + var2;
    
  • Ora gioca con i puntatori con un po 'di codice. Abituati a queste caratteristiche dei puntatori ora. Fino a questo punto, stiamo parlando di cosa fanno i puntatori. Una volta che sei d'accordo, quindi inizia a studiare come i puntatori indicano una variabile e come reagiscono se vengono applicate le normali operazioni aritmetiche. Quindi andare per la relazione tra puntatori e matrici e puntatori a puntatori.

Questo è tutto ciò che suggerirò sui suggerimenti per l'apprendimento.


Volevo seguire il processo "prima cosa fa e poi come". Astrazione!
Gulshan,

5

Modificato a supporto del requisito rivisto della domanda

Il mio percorso verso la comprensione "post-pointer" (se ricordo bene) è andato così. Ho avuto una semplice esperienza di programmazione di assemblaggio da quando ero ancora in giro con un BBC Micro, quindi ho avuto il concetto di memoria come un mucchio di scatole (vedi sotto per questo). Ciò è stato rafforzato dall'uso di array. Tuttavia, stavo andando nel mondo di C e dovevo occuparmi di stringhe e questo significava indicatori. In BASIC questo è stato banale, in assemblatore non ho mai dovuto lavorare con loro, e ora nel classico C sono tutti puntatori e cose del genere. Ah, ma posso tornare indietro alle matrici con stringhe (con terminazione null) come questa:

char s[] = "Hello, world!";
printf("%s",s);

Va bene, è solo una serie di caratteri (8 bit per personaggio nel mio piccolo mondo) con un carattere zero alla fine per mostrare dove finisce. Printf prende semplicemente quell'array e lo scorre sopra stampandolo. E se volessi passare questa stringa in una funzione?

void print_str(char* p) {
  printf("%s",p);
}

È quello che dice il manuale, ma di che si tratta? Hmm, char * significa "puntatore a un carattere". OK ... mi ha perso. Poi ho capito che il carattere * rende p equivalente a s [0]. OK, posso usarlo, ma non ho ancora modificato i puntatori. Voglio dire, come posso impostare alcuni dati con una di queste cose? Di nuovo al manuale ...

char* p = "Hello World!";

Mentre scrivo quanto sopra sto dicendo a me stesso "dichiarare un puntatore a un carattere e impostarlo uguale a questa matrice di caratteri". In qualche modo quel puntatore elimina la necessità di un array. Immagino sia il compilatore che sta facendo delle cose per me per cambiare. Quindi, come posso cambiare l'array con questo puntatore? So che potrei usare la versione dell'array

 s[2] = 'L'; 

ma qual è l'equivalente in "pointer speak"? Di nuovo a quel manuale ...

*(p+2) = 'L';

Suppongo che * significhi semplicemente "il contenuto dell'indirizzo di memoria" ed (p+2)è s[2]. Il che significava che il puntatore p era ... solo ... un ... indirizzo ... bong!

Che c'è il suono dell'illuminazione. Improvvisamente ho modificato i puntatori (è stato molto tempo fa, noi vecchi timer non abbiamo "grugnito" fino a dopo). Era solo indiretto.


Originale:

OK, il mio valore 2c:

Immagina che la memoria sia un mucchio di scatole. Ogni casella ha un numero sul lato (l'indirizzo). Ogni casella contiene un numero (il contenuto). Puoi lavorare con queste caselle in 2 modi: variabili (voglio il contenuto della casella N), puntatori (voglio il contenuto della casella qualunque cosa sia nella casella N ). I puntatori sono semplicemente indiretti.

E per la grande risposta che copre tutto ciò che avrai mai bisogno di sapere - leggi questo .


++ È più o meno il modo in cui ho insegnato la materia.
Mike Dunlavey,

Articolo modificato per supportare i requisiti OP.
Gary Rowe,

Quale manuale stavi usando per aggiungere altri caratteri NUL alla fine delle tue stringhe?
Rob Gilliam,

@raimesh Uno molto vecchio (intorno al 1991). Non ricordo quale.
Gary Rowe,

1991? Non è vecchio per un manuale in linguaggio C: la prima edizione di K&R è stata pubblicata nel 1978! Ho solo incuriosito che ci sia mai stato un manuale che suggeriva la necessità di inserire \ 0 alla fine delle costanti della stringa. (In realtà, guardandolo di nuovo, intendevi mettere \ n?)
Rob Gilliam,

4

Prendi dei piccoli blocchi di legno.

aggiungi ganci di metallo a un'estremità e occhi di metallo all'altra.

ora puoi fare un elenco collegato in cose con cui puoi giocare.

Prova a spiegare con questo sostegno fisico. Spesso desideravo avere questo quando insegnavo puntatori agli studenti del primo anno (matricole).

Il piccolo gancio di metallo è il puntatore, il blocco di legno a cui puntava la cosa.

DEFINO a chiunque di non ottenerlo dopo aver giocato con i blocchi.


4

Ho semplificato la mia vita quando ho appena rimosso tutta la lanugine e ho iniziato a trattare il puntatore come qualsiasi altra variabile piuttosto che come qualche entità magica (molto tempo fa in classe 11) .. basta sapere 3 cose:

  1. Il puntatore è una variabile che memorizza l'indirizzo di un'altra variabile (o semplicemente qualsiasi indirizzo).
  2. * viene utilizzato per ottenere il valore nella posizione di memoria memorizzata nella variabile puntatore.
  3. & operator fornisce l'indirizzo di una posizione di memoria.

Il resto è zucchero sintattico e buon senso. Basta scrivere alcuni semplici programmi in C (come implementare una libreria di elenchi collegati) usando i puntatori per capire.


2
  1. Il puntatore può essere pensato come una generalizzazione di un indice in un array.
    • Considera che un array grande può essere suddiviso in un numero di array più piccoli, non sovrapposti, di dimensioni variabili. Ora, questi array più piccoli sono ciò che di solito pensiamo come array. Quello più grande è quindi l'intero spazio di memoria del computer. Il processo di troncatura di array più piccoli si chiama allocazione della memoria.
  2. Un insieme di strutture che sono collegate tra loro da alcuni puntatori può essere pensato come un grafico diretto .
    • Ogni vertice è una variabile che può contenere un valore.
    • Alcune variabili sono puntatori e ogni puntatore può avere esattamente un bordo in uscita in qualcos'altro.
    • Le variabili che non sono puntatori non avranno alcun bordo in uscita. Potrebbero avere un numero qualsiasi di edge in entrata.

2

Non riesco proprio a ricordare le circostanze intorno ai miei puntatori-aha-moment, ma ho retroattivamente modificato la memoria attorno alla mia comprensione di un array in stile c. (cioè arr[3]è uguale a *(arr+3))

Per qualche ragione trovo immensamente utile visualizzare i puntatori come matrici ogni volta che mi capita di trovarmi in una situazione di puntatore.


2

Fondamentalmente @Gary Rowe ha introdotto il modello giusto. Memoria come un insieme di caselle con indirizzi (numeri) su di essi. Ogni casella memorizza un valore (numero).

L'idea di un puntatore è di interpretare il valore in una casella come l'indirizzo di un'altra casella. Questo valore viene utilizzato per fare riferimento a una casella specifica, motivo per cui viene chiamato riferimento . Quindi la dereferenziazione è il processo di apertura della casella a cui si fa riferimento.

if vè una variabile (casella), quindi l'istruzione

  • vsignifica valore di v, cioè dammi quello che c'è nella scatola
  • *vsignifica dereference il valore di v, ovvero dammi ciò che è nella casella a cui fa riferimento il valore div
  • &vsignifica riferimento v, cioè dammi l'indirizzo sulla scatola

Penso che non serva allo scopo di introdurre i puntatori come qualcosa di completamente diverso. Era un concetto difficile da capire per me quando ero un bambino. Sembrava sempre una magia oscura che non avevo mai veramente capito, che aveva bisogno di molti personaggi speciali. La prima volta che ho capito è stato quando ho scritto un piccolo gioco usando la doppia indiretta in una lingua che non ha l'aritmetica del puntatore. Questo mi ha illuminato.

I puntatori sono una questione di interpretazione. Penso che spiegare ciò renda le cose molto più facili. E l'aritmetica del puntatore sono operazioni estremamente semplici e intuitive se mostrate un semplice esempio con una memoria di 10 variabili.


2

La spiegazione che mi è davvero piaciuta è stata:

Considera una griglia di città con case diverse costruite su pezzi di terra. Tieni in mano un pezzo di carta. Sulla carta hai scritto:


La casa di David,

112 Così e così via.


Il pezzo di carta (variabile puntatore) contiene un indirizzo che punta alla casa di David. Quando vuoi dire a un amico di dare un'occhiata alla bella casa di David, è molto più facile usare il pezzo di carta come riferimento alla casa piuttosto che inviare il vero edificio a due piani per posta.

Come con i puntatori reali, potresti avere dei problemi quando segui l'indirizzo sul tuo foglio di carta. David avrebbe potuto muoversi e quando arrivi lì trovi solo un grosso buco nel terreno. In questo caso sarebbe stato meglio cancellare l'indirizzo sul foglio quando David si è spostato o almeno cambiarlo con quello nuovo. Potresti anche scoprire che vai all'indirizzo e inserisci quello che pensi sia il soggiorno del tuo amico David, ma questa volta finisci in una piscina completa per sconosciuti. Qualcun altro ha usato lo spazio all'indirizzo che hai avuto per qualcosa di completamente diverso.


2

Se vuoi spiegare i puntatori devi prima spiegare la memoria. Di solito lo faccio usando carta millimetrata / carta quadrata con righe e colonne. Se lo "studente" capisce la memoria, può capire cos'è un indirizzo. Se hai l'indirizzo hai puntatori.

Puoi giocare con questa astrazione. Ad esempio, scrivere l'indirizzo (numero) di un quadrato in un altro quadrato. Ora disegna una freccia dal quadrato del puntatore al quadrato di destinazione. Ora sovrascrivi il puntatore (es. Incrementalo) e regola la freccia. Scrivi un indirizzo in un'altra casella e lascia che lo studente disegna la freccia ...

Passaggio successivo: assegna un nome a determinati quadrati (come il puntatore). Ora puoi spiegare la dereferenziazione. Questo è tutto.


2
Mi ricorda un "computer" che ho usato prima che potessi mettere le mani su uno che effettivamente utilizzava gli elettroni: il cartone "CARDIACO" penso che sia stato chiamato. Letteralmente, scrivere i numeri in caselle numerate per ottenere numeri da altre caselle numerate. È stato educativo e non ho mai avuto problemi a comprendere i puntatori su microprocessori reali.
DarenW,

2

Forse sono solo io, ma lavoro bene con le analogie. Quindi supponiamo che tu abbia un amico (funzione / classe) "Foo", che vuole avere qualcun altro (funzione / classe diversa), "Bar", contattarti per qualche motivo. "Foo" potrebbe portarti a vedere "Bar", ma non è conveniente spostare tutti quegli esseri (istanze) in giro. Tuttavia, "Foo" potrebbe inviare a "Bar" il tuo numero di telefono (puntatore). In questo modo, indipendentemente da dove ti trovi, "Foo" sa come contattarti senza doverti trovare.

E, diciamo, "Bar" ha una conoscenza, "Baz", che vuole anche mettersi in contatto con te. Ma sei protettivo del tuo numero di telefono e non vuoi che tutti lo abbiano, "Baz" può chiamare "Bar" (telefono come puntatore), che può quindi inoltrarti la chiamata (un altro puntatore). E così via lungo la catena degli amici di "Baz" e degli amici di amici.


2

I puntatori hanno molto più senso se hai studiato il linguaggio assembly e / o l'architettura del computer. Penso che se insegno a una classe C, inizierei con un paio di settimane di architettura per spiegare il modello di memoria e il tipo di istruzioni che il processore esegue effettivamente.


Assolutamente. Quando ti occupi di registri e comprendi cose come i valori immediati e l'indirizzamento indiretto, l'intero concetto di puntatori è intuitivo. Ho imparato l'assemblaggio 6502 e poi l'assemblaggio 68K prima di scrivere la mia prima riga di C, quindi quando sono stato introdotto i puntatori, ho guardato l'assemblaggio (intermedio) del compilatore ed era esattamente come previsto.
Radian,

2

Il mio "aha!" è arrivato il momento in questo tutorial:

Un tutorial su puntatori e matrici in C

Per l'esattezza, è arrivato in questo capitolo: Capitolo 3: Puntatori e stringhe

Per essere ancora più precisi, è arrivato con questa frase:

Il parametro passato a put () è un puntatore, ovvero il valore di un puntatore (poiché tutti i parametri in C sono passati per valore) e il valore di un puntatore è l'indirizzo a cui punta, o, semplicemente, un indirizzo .

Quando l'ho letto, le nuvole si sono separate e gli angeli hanno suonato fanfare della tromba.

Perché, vedi, ogni tutorial o libro di C che avevo letto prima affermava che C poteva passare per valore o per riferimento, una bugia nefasta. La verità è che C passa sempre per valore, ma a volte il valore passato sembra essere un indirizzo. All'interno del metodo, viene creata una copia di quell'indirizzo, proprio come una copia di un int trasmesso. Una copia non viene fatta del valore a cui punta il puntatore. Pertanto, utilizzando il puntatore all'interno del metodo è possibile accedere al valore originale e modificarlo.

Non sono mai diventato un programmatore C, ma sono diventato un programmatore .NET e gli oggetti e i riferimenti agli oggetti funzionano allo stesso modo; il riferimento all'oggetto viene passato per valore (e quindi copiato), ma l'oggetto stesso non viene copiato. Ho lavorato con molti programmatori che non lo capiscono perché non hanno mai imparato i puntatori.


1

Il trucco è spiegare che la posizione di una cosa e la cosa stessa non sono le stesse, proprio come l'immagine di una pipa non è una pipa . Quando sposti una cosa, la sua posizione cambia. La posizione rimane e qualche altra cosa può essere messa lì.


1

Un puntatore è una nota adesiva che ti dice dove si trova qualcosa di utile. Contiene la posizione della cosa e ti dice quanto è grande la cosa (in C, comunque). Quindi un doppio puntatore è come una nota adesiva che dice "C'è un pacchetto da sei in frigo". Devi effettivamente andare a prendere il pacchetto da sei per capire se si tratta di Coca-Cola o Budweiser.


1

Dato il codice:

int v=42; // dichiara e inizializza una variabile semplice

int *p = &v; // creando un punto p per la variabile v

Di seguito si può dire del codice sopra:

int * p // "int pointer p" ... ecco come dichiarare un puntatore a una variabile di tipo int

*p // "indicato da p" ... ovvero i dati a cui p punta, è uguale a v.

&v // "indirizzo della variabile v" ... rappresenta il valore letterale di p


1

http://cslibrary.stanford.edu/

Questo sito ha grandi tutorial per l'apprendimento di puntatori e gestione della memoria.

Ti suggerirei di consultare le basi dei puntatori, i puntatori e la memoria forniti sul sito. È inoltre possibile esaminare i problemi relativi agli elenchi collegati indicati sul collegamento per rafforzare ulteriormente i concetti del puntatore.


1

La chiave per spiegare i suggerimenti è assicurarsi che le persone a cui stai spiegando, abbiano già una comprensione del concetto di memoria. Sarebbe bello se lo capissero davvero a basso livello, credere che la memoria esista come un array enorme e capire che è possibile accedere a qualsiasi posizione dell'array dalla sua posizione di indice è sufficiente.

Il passo successivo, avere il concetto di passare la posizione dell'indice piuttosto che copiare l'intera memoria ha senso per la maggior parte delle persone. E questo è sufficiente per consentire alla maggior parte delle persone di capire perché i puntatori sono utili.

il passaggio finale per comprendere i puntatori è spiegare come è possibile passare come parametro una posizione di indice di memoria per il metodo per memorizzare la posizione di indice per la quale sono memorizzati tutti i dati. Ho scoperto che questo può essere un passo troppo avanti per alcune persone da affrontare.

Una volta che qualcuno ha afferrato questi passaggi di base, è subito chiaro per loro che è possibile concatenare i puntatori a tempo indeterminato, purché si tenga traccia di quante volte è necessario guardare gli indirizzi per trovare l'oggetto dati effettivo.

Una volta che qualcuno ha afferrato i puntatori, la prossima cosa che devono afferrare rapidamente è la differenza tra la memoria dell'heap e la memoria dello stack e perché i puntatori alla memoria dello stack sono pericolosi se passati al di fuori del metodo.


0

Ricordo un libro "C puzzle" o simile, che ho letto puramente perché era uno dei pochi libri di programmazione / computer disponibili in biblioteca, la mia comprensione di C era rudimentale. Ti ha lanciato una spia C e ti ha chiesto di esplorarlo, diventando sempre più complicato.


0

Quando ero all'università, il mio professore aveva delle diapositive powerpoint davvero ordinate che descrivevano un punto come una variabile separata da solo con una freccia in una posizione di memoria (rappresentata come un array) e quando stavamo facendo liste collegate, lo faceva fare un passo - passo dopo passo, mostrando quando la freccia cambia, quando il puntatore è deferenziato, ecc ..., non è stato possibile capirlo entro un paio di minuti. Il concetto in sé è davvero semplice, ma farlo nel modo giusto o applicarlo in programmi pratici richiede più pratica.


0

Prima di farlo, spiego che nella programmazione "tutto utilizza la memoria" e l'allocazione di variabili (statiche) in memoria. Spiegherò anche cos'è un indirizzo di memoria e la relazione tra spazio di memoria, indirizzo di memoria e variabili.

Spiego infine che esistono tipi di dati e variabili interi, tipo di dati stringa e variabili ... e così via, fino a quando non viene spiegato che esiste un tipo di dati speciale che memorizza gli indirizzi di memoria, che ha un valore vuoto come 0 o "", chiamato null .

E, infine, variabili allocate dinamicamente attraverso l'uso di puntatori.

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.