Ciao e bentornato.
In questa conferenza, voglio parlare del
caso d'uso dei puntatori quando li useremo come li useremo Sappiamo come crearli.
dichiararli e definirli.
Sappiamo come assegnare loro l'indirizzo di una variabile esistente utilizzando l'indirizzo dell'operatore.
E sappiamo come de-referenziarli, come
ottenere il valore a cui punta il puntatore.
Ora come iniziamo effettivamente a usarli.
Quindi c offre alcune diverse operazioni di base che puoi eseguire sui puntatori.
Puoi assegnare un indirizzo a un puntatore
che abbiamo già imparato.
Questa è una variabile esistente in cui la variabile ha un indirizzo.
E basta guardare l'indirizzo di quella variabile usando l'indirizzo dell'operatore, e
abbiamo ragione a questo. Potremmo anche assegnare un puntatore esistente a un altro puntatore.
Sono due i modi in cui puoi assegnare un indirizzo a un puntatore.
Quindi può essere un nome di array da solo.
Non abbiamo ancora parlato di array, ma quando assegni un rilancio a un puntatore
tutto ciò che devi fare è specificare il nome. Non devi nemmeno usare l'indirizzo dell'operatore.
Finora ci siamo concentrati solo sull'utilizzo di una variabile esistente
e sul passaggio dell'indirizzo della variabile stessa.
Ma i 3 modi in cui puoi farlo sono menzionati qui.
Puoi anche dereferenziare un puntatore. Abbiamo appena scoperto che è lì che usi,
l'asterisco l'operatore di riferimento indiretto.
Questo ti darà il valore che è memorizzato nel puntatore a. Il puntatore il valore del
puntatore che è un indirizzo a cui punta
il punto, usalo. Puoi anche prendere un indirizzo di puntatore.
Questo è l'indirizzo effettivo del puntatore quando si crea il puntatore
in cui è memorizzato il puntatore stesso.
Puoi anche usare l'indirizzo che ho operatore su quel puntatore.
E puoi anche eseguire operazioni aritmetiche con i puntatori. Avremo una conferenza separata su questo.
Qui è dove puoi usare gli operatori più e
meno per aggiungere numeri interi a un puntatore o sottrarre numeri interi da un puntatore.
E questo ti permette di avere una notazione specifica e ti permette
di saltare in giro nella memoria più velocemente, specialmente se stai usando gli array.
Quindi quello che fai in genere è quando aggiungi un numero
intero a un puntatore usando
l'operatore più, si limiterà
a impostare l'indirizzo di quel puntatore su così tanti byte.
Se sottrai
un numero da un puntatore, imposterà
quell'indirizzo meno di un certo numero di byte.
Puoi anche aumentare il decremento di uno
e vedrai che questo è molto comune quando lo usiamo negli array.
Quindi, quando sei in un array e vuoi passare all'elemento successivo invece di usare l'indice, puoi
usare un puntatore e semplicemente incrementarlo di uno.
Se vuoi passare all'elemento precedente invece di usare un indice puoi
semplicemente decrementarlo puntando di uno.
Quindi, di aritmetica dei puntatori, parleremo esclusivamente, ma è qualcosa
che puoi fare con i puntatori.
E ancora, è una notazione particolare che ti permette di muoverti
velocemente nella memoria, essere
più efficiente.
Puoi anche trovare la differenza tra 2 puntatori.
Quindi, se hai 2 puntatori e gli elementi sono nello stesso
array, potresti voler scoprire quanto sono
distanti, gli elementi, puoi eliminarli.
Questa sarebbe la differenza di
2 puntatori e. Quindi se sei indice 5 e
poi hai un punto puntatore nell'indice 5, hai un altro punto
che punta all'indice 15, potresti
meno e questo
ti darebbe quanti elementi sono lontani, perché sarebbe l'indirizzo esadecimale
meno l'altro indirizzo esadecimale assumendo l'array in
memoria continua quale è, mostrerà quanto sono distanti.
È possibile utilizzare operatori relazionali per confrontare i valori di 2 puntatori.
I puntatori devono essere dello stesso tipo. Quindi, se sono entrambi numeri interi, devono essere
entrambi dello stesso tipo.
Questo ti dirà solo se gli indirizzi sono maggiori o minori di.
Ancora una volta, potresti identificare in quale parte dell'array ti trovi.
Esistono anche 2 forme di sottrazione.
Quindi ho appena parlato della sottrazione
in cui puoi effettivamente prendere un puntatore e sottrarlo da un altro
che otterrai
un puntatore oppure puoi sottrarre un puntatore da
un numero intero, che ti darà un puntatore.
Quindi sottrai un puntatore da un altro per ottenere un numero intero o
per sottrarre un intero da un puntatore per ottenere un puntatore. Questi sono i 2 casi.
Quindi terremo presente che esamineremo alcuni altri esempi.
Prestare attenzione quando si utilizza l'incremento o il decremento dei puntatori
e la matrice fuori dai limiti. Quindi la maggior parte di queste cose
che fai con l'aritmetica dei puntatori è quando operi su array.
Perché ancora una volta, ti consente di muoverti rapidamente, è una sintassi semplice.
Ma ricorda, gli array hanno un errore di fuori limite.
Quindi, se aumenti troppo, uscirai
dai limiti nell'array. Se decrementi troppo, avrai
un limite inferiore, entrerai in un numero negativo.
Il computer stesso non terrà traccia del fatto che un puntatore punti ancora a un elemento dell'array.
Quindi, usando il punto aritmetico sugli array, devi stare attento.
Se vuoi usare i puntatori nelle espressioni, puoi farlo anche tu.
Quindi ne abbiamo parlato un po 'prima, e abbiamo dereferenziato il puntatore e abbiamo aggiunto 15
ad esso. Penso che ne abbiamo 20.
Quindi il valore del puntatore pi greco può essere utilizzato nelle espressioni aritmetiche.
Quindi, se una variabile è definita come di tipo puntatore a numero
intero, viene valutata utilizzando le regole dell'aritmetica dei numeri interi.
E lo abbiamo dimostrato con la dereferenziazione. Quindi, se hai
una variabile e hai un puntatore, inizializzalo su null, quindi firmi il
tuo puntatore con l'indirizzo di quella variabile. Quindi puoi semplicemente dire numero stella più uguale a 25.
Ricorda, il numero stella sta ottenendo il valore che punta.
Quindi ciò che sta facendo è dire che il valore di pnumber
è uguale al valore pnumber più 25.
Quindi il valore sarà uguale a 35.
Questo non sta cambiando ciò a cui punta perché stiamo usando l'asterisco, quindi non
sta cambiando l'indirizzo a cui stiamo
puntando, non sta cambiando l'indirizzo stesso perché non abbiamo l'indirizzo dell'operatore.
Stiamo cambiando il valore a cui punta.
Quindi, incrementando il valore della variabile numerica di 25, la stella indica che stai
chiedendo il contenuto a cui punta la variabile chiamata pnumber.
Se un puntatore punta alla variabile x, quel puntatore
è stato definito come un puntatore allo stesso tipo di dati di x.
Quindi, se crei un puntatore finale un puntatore float, puoi solo
avere un indirizzo di un
tipo di dati che è float o int a seconda di quello che hai dichiarato.
Quindi l'uso dell'operatore di riferimento indiretto
in un'espressione è identico all'uso di x nella stessa espressione.
E questo è tutto ciò che sta dicendo è che se usi un float o
hai un int e usi l'operatore di riferimento indiretto, lo dereferisci, puoi fare
tutto ciò che puoi se hai un float in esso, qualunque cosa stia puntando.
Una variabile definita come un puntatore a un int può memorizzare l'indirizzo di
qualsiasi variabile di tipo in, lo sappiamo.
Quindi qui il valore int è 999, possiamo assegnare quell'int a pnumber fintanto
che pnumber è un puntatore a un int, e
quindi possiamo aggiungerlo.
L'istruzione funzionerà con il nuovo valore della variabile
e il nuovo valore del contenuto sarà 1024.
Pnumber punta al valore,
l'indirizzo del valore. Quindi, se modifichi il valore
del puntatore a cui punta, cambierai il valore di questa variabile.
Quindi ricorda che questo è il concetto di cambiare una variabile in un punto
il puntatore e lo stai cambiando in più variabili.
Un puntatore può contenere l'indirizzo di qualsiasi variabile del tipo appropriato.
Quindi puoi usare una variabile puntatore per cambiare i valori di molte variabili diverse
se ci sono interi, doppi o float. Finché sono del tipo compatibile con il tipo di
puntatore, è possibile modificare quel valore. Non è possibile modificare il
valore pnumbers se pnumber punta a un float e il valore
è un numero intero. Non saresti in grado di farlo. Avresti un errore di compilazione.
Un altro esempio qui. Qui ti mostreremo
come ottenere l'indirizzo di alcune variabili. Quindi qui in questo main, stiamo facendo
un po 'di
aritmetica dei puntatori sulle espressioni effettive.
Quindi non stiamo facendo nulla sull'array, quindi non stiamo incrementando o decrementando i
puntatori in questo. Quindi stiamo diminuendo i valori. Quindi abbiamo 2 tipi
long num1 e num2 e quindi abbiamo un puntatore di tipo long, lo inizializziamo a null.
Assegniamo l'indirizzo di num1 al nostro puntatore pnum.
E poi assegniamo il valore di pnum a L,
quindi significa che num1 ora sarà 2l.
Se incrementiamo num2, ora num2 non ha ancora
fatto nulla.
Quindi il valore di pnum è 2l, ciò significa che il valore di num1 è 2l.
L è solo il suffisso per indicare che è lungo.
Se ora incrementiamo il valore lungo num2,
è 0l, sarà 1l.
Se diciamo num2 più è uguale
a stella pnum, pnum è 2l, quindi sarà
1l più 2l, num2 ora sarà uguale a 3l.
E poi se assegniamo l'indirizzo di num2 a pnum,
pnum sarà ora uguale all'indirizzo di num2.
Quindi se incrementiamo pnum ciò
a cui stiamo puntando, ora avremo 4l.
E lo esamineremo all'interno dei blocchi freddi, ma ti
sto guidando attraverso di esso. Num1 sarà uguale a 2l.
Num2 sarà uguale a che punta
a pnum.
Pnum è 3l, quindi penso che sarà 3l.
Pnum sarà 3l, quindi pneum plus num2
sarà 6l.
Quindi vediamo se ho ragione, esaminalo in blocchi di codice.
Quindi eccoci in blocchi di codice, abbiamo provato a passare attraverso questo a mano,
ma ora vogliamo vedere se abbiamo ragione. Usiamo il debugger.
Lo metteremo qui e vedremo mentre procediamo nel programma come cambiano i valori.
Quindi costruiamolo ed eseguiamo il debug.
Questo inizierà e si fermerà alla riga 10.
E se apriamo la nostra finestra di controllo,
cosa che possiamo fare eseguendo il debug in Windows Watches, Watches, possiamo
vedere i valori mentre procedono. Quindi in questo
momento num1 è zero, num2 è zero, pnum punta a null.
Ecco perché è 0x0,
perché sappiamo che null rappresenta 0 in pnumber.
E possiamo usarlo quando lo confrontiamo nelle istruzioni if.
Quindi, dopo aver eseguito la riga 10, pnum dovrebbe ora avere l'indirizzo di num1.
Quindi facciamo un passo avanti.
Ora pnum ha un indirizzo effettivo 0x60ff08.
Dopo aver eseguito la riga 11, diremo che il valore di pnum è uguale a 2l, e non vedremo effettivamente
nulla nella finestra di controllo per questo. Ma andiamo avanti ed eseguiamolo, pnum ha
ancora lo stesso indirizzo.
Num1 è ora cambiato perché pnum aveva l'indirizzo
di num1 e l'abbiamo cambiato.
Quindi ora num1 è 2. Ora nelle righe 12, incrementeremo num2.
Quindi num2 diventerà 1l.
Sarà 1. L'indirizzo di pnum è sempre lo stesso.
Ora aggiungeremo a num2 qualunque sia
stato pnum. Pnum ricorda punta
a num1, quindi num2 dovrebbe essere 3.
Quindi andiamo a 14, num2 è 3, num1 è ancora 2.
Ora alla riga 15, assegneremo
a pnum un nuovo indirizzo di num2. Quindi, dopo
questo, non saremo più 0x60ff08.
Saremo 060ff04.
Quindi ora se incrementiamo il valore a cui punta pnum che è il numero 2
e il numero 2 è 3, il numero 2 ora dovrebbe essere 4.
E vediamo che è 4.
Ora stamperemo tutti questi valori e vedrai che il valore di num1 dovrebbe
essere 2.
Il valore di num2 dovrebbe essere 4.
Pnum punta a num2, quindi dovrebbe essere ancora 4.
E poi diremo il valore di pnum che è 4 più, num2
che è 4 dovrebbe essere 8.
Quindi potrei essere stato effettivamente fuori con 6.
Quindi ora non saremo in grado di vederlo nella finestra dell'orologio. Quindi giochiamo e
vediamo l'output come istruzioni printf. Dovremmo vedere 2448.
Quindi eseguiamolo ora in modalità non di
debug e vediamo 2 4 4 8.
Quindi eccoci qui. Spero che abbia senso.
Torniamo indietro e continuiamo la nostra discussione.
Allora, che ne dici di quando riceviamo input, come possiamo usare i puntatori in quel modo.
Finora, quando usiamo scanf per inserire valori, dobbiamo usare l'operatore e commerciale ogni volta che abbiamo
un tipo di dati inter o float. L'unica volta che ce l'abbiamo è quando abbiamo
un array di caratteri e il motivo è perché il carattere a
è in realtà un puntatore di carattere di cui parleremo.
Ma se usiamo scanf per inserire valori e li usiamo su normali tipi
di dati che non sono array di caratteri, abbiamo sempre dovuto usare questo operatore add o sub.
E la ragione di ciò è che il secondo argomento di scanf è un puntatore.
Quindi dobbiamo inviargli un indirizzo. I puntatori hanno solo valori di indirizzi.
Quindi, quando hai un puntatore che contiene già un indirizzo, puoi usare il nome del puntatore come argomento per scanf.
Quindi ora non devi usare l'operatore e commerciale se hai effettivamente creato un
puntatore, perché il secondo argomento di scanf è un puntatore e quindi puoi
semplicemente dire il nome del puntatore.
In passato, se volevamo memorizzare un valore in value,
avremmo detto valore e
commerciale nel secondo argomento di scanf.
Ma se abbiamo un puntatore che creiamo e già punta a
un indirizzo come in questo esempio,
allora quando usiamo scanf, tutto quello che dobbiamo dire è il nome del valore,
il nome del puntatore.
Quindi ecco un caso in cui abbiamo un numero intero, ma
non dobbiamo usare la e commerciale perché abbiamo un puntatore a un numero intero.
Diciamo solo percentuale d, valore p.
Quindi ci sono altre volte in cui puoi passare
nomi regolari di variabili a scanf oltre agli array di caratteri se
hai un puntatore.
Puoi anche stampare ciò che hanno inserito in seguito e noterai
che il valore verrà effettivamente modificato in qualsiasi cosa abbiano
inserito perché stiamo puntando a quell'indirizzo.
Verifica di null.
C'è una regola che devi imprimere nella tua memoria. Questo è molto, molto importante.
Abbiamo parlato di dereferenziazione, ma cosa succede se si dereferenzia un puntatore
non intenzionale, un puntatore che punta a nulla o
un puntatore a cui non è allocata memoria.
Quindi cosa succede se facciamo qualcosa di simile. Dichiariamo un puntatore,
ma non lo abbiamo mai inizializzato, quindi proviamo ad assegnargli.
Decifriamo e assegniamo ad esso, è un grosso errore.
Quindi la seconda riga significa fondamentalmente che si
desidera memorizzare il valore 5 nella posizione a cui
punta pt, ma pt non punta a nulla perché non è stato inizializzato.
Quindi pt ora avrà un valore casuale.
Quindi non si sa dove verrà posizionato 5, potrebbe essere posizionato ovunque nella memoria.
Questo potrebbe essere innocuo. Potrebbe sovrascrivere i
dati esistenti o le variabili di stato esistenti nel programma.
Potrebbe causare l'arresto anomalo del programma. È molto problematico.
Pertanto, quando si crea un puntatore e si alloca la memoria per memorizzare il puntatore stesso,
non alloca memoria per memorizzare i dati.
Quindi, dicendo instar pt, quella memoria
allocata per il puntatore, ma non allocava
memoria per cosa per il valore
del puntatore, l'indirizzo. Ricorda, i puntatori contengono indirizzi.
Ha creato il puntatore ma non ha creato alcuna memoria per ciò a cui punta.
Quindi non puoi dereferenziarlo.
Quindi non alloca memoria per memorizzare i dati.
Prima di usare un puntatore, gli dovrebbe essere assegnata una posizione di memoria cheèstata
già allocata o devi usare l'allocazione dinamica della memoria usando malloc di cui parleremo
Quindi o assegni l'indirizzo di una variabile esistente al puntatore o
lo usi malloc che è l'allocazione dinamica della memoria.
Ma tienilo a mente ogni volta che usi un puntatore se lo
hai creato ed era nullo, vuoi verificarlo in anticipo
per nullo, ne parleremo tra un secondo. Ma se hai creato un
puntatore che non è stato inizializzato, non assegnargli nulla a
meno che non punti a un'altra variabile inizializzata in quel modo o che abbia memoria allocata.
Quindi è qui che entra in gioco il null.
Puoi effettivamente testare null per assicurarti di
non avere un errore di dereferenziazione. Assicurati che il tuo programma non si blocchi.
Quindi sappiamo già che null praticamente non punta
a nulla e dovremmo inizializzare tutte le nostre variabili, i
nostri puntatori a null.
Quindi dovremmo sempre fare qualcosa del genere.
Null è un simbolo speciale in c che rappresenta il puntatore equivalente
a zero con numeri ordinari.
Il seguente imposta anche un puntatore a null utilizzando zero.
Quindi possiamo anche se non vogliamo passarlo o assegnarlo a null, possiamo semplicemente dire 0 e
questo è l'equivalente di dire che punta a nulla.
Quindi, poiché null è l'equivalente di 0,
se vuoi verificare se il valore p è nullo, puoi fare qualcosa
come usare test espliciti. Puoi dire che il puntatore è uguale a null.
E se il puntatore è uguale a null, il che significa
che è zero, allora puoi effettivamente fare qualcosa.
Quindi, se non è zero o non è nullo, è un valore vero o qualsiasi altro valore,
questo è un modo per verificare che non si verifichino arresti anomali.
Assicurati prima di provare a dereferenziare qualcosa,
questo ti dirà se ha memoria allocata o meno.
Quindi si desidera verificare la presenza di null prima di dereferenziare un puntatore.
E spesso lo fai nelle funzioni
perché le funzioni prenderanno i puntatori come parametri.
E se il chiamante
sta trasmettendo dati errati o passando un puntatore non ha alcuna memoria
allocata, potrebbe bloccare il codice. Quindi quello che vuoi fare è assicurarti
che il chiamante abbia un indirizzo valido in modo da poterlo controllare per null.
Fondamentalmente, cosa significa avere sempre questi se controlla per
dire non valore p o non puntatore.
E se non è un puntatore, puoi dereferenziarlo.
Stiamo parlando molto di suggerimenti. Abbiamo discusso parecchio.
Sono sicuro che sta diventando un po 'opprimente. Potresti prendere una pausa e
seguire le lezioni precedenti cercando di sincronizzare davvero questo concetto nella tua testa.
I puntatori contengono solo indirizzi.
Ma ci sono 3 parti, quindi può diventare problematico.
Ma rimani positivo. Resta con me e so che
è fonte di confusione.
Abbiamo parlato di come lavorare con gli indirizzi.
Abbiamo parlato di lavorare con i valori. Abbiamo parlato di lavorare con il puntatore
stesso e abbiamo parlato di lavorare con variabili che puntano al puntatore.
Quindi ci sono tutte queste
cose che devi ricordare e possono essere molto molto travolgenti e frustranti.
Ed è sempre difficile cercare di capire cosa sta succedendo e ci vorrà molta pratica.
Il modo migliore per comprendere questo concetto e tutto ciò con cui abbiamo parlato
fino ad ora è fare
pratica, scrivere brevi programmi che utilizzino tutti i concetti che ho menzionato.
Quindi scrivi un programma che ottiene
i valori usando un puntatore, forse usando l'operatore di
riferimento indiretto, dereferenziazione. Scrivi un programma che modifichi i valori usando i puntatori.
Quindi questo sarebbe dereferenziare, ottenere i valori usando i puntatori sarebbe solo
il nome del puntatore che sarebbe l'indirizzo. Stampando indirizzi, abbiamo esaminato tutta quella sintassi.
Pratica questa roba ancora e ancora e ancora, è così che diventerai bravo.
Devi aumentare la tua fiducia e devi studiare questi concetti
in dettaglio per padroneggiarli.
Abbiamo ancora molto di cui parlare di suggerimenti, ma questo potrebbe essere il momento di allontanarsi e ripassare
ciò di cui abbiamo parlato finora in modo che sia trapanato nella tua testa.
Grazie.