Ciao e bentornato.
In questa conferenza, voglio parlare dell'uso di puntatori e strutture,
come sono correlati.
Possiamo avere elementi all'interno di una struttura come
puntatori, e quindi possiamo anche fare in modo che le nostre stesse strutture siano puntatori.
Due concetti molto utili.
Quindi la struttura è importante qui c consente di puntare due strutture.
Quindi, quando creiamo una variabile di struttura di tipo, possiamo
crearla come un puntatore.
I puntatori alle strutture sono più facili da manipolare rispetto alle strutture stesse.
Quindi, di nuovo, è una specie di notazione,
rende le cose molto più semplici ed è anche più efficiente.
In alcune vecchie implementazioni del linguaggio c,
una struttura non
può essere passata come argomento a una funzione, ma
sappiamo che può farlo un puntatore. Quindi questo è un altro motivo
per cui vuoi che una struttura sia un puntatore quando
la dichiari perché poi puoi passarla in giro nelle versioni precedenti
di c puoi passarla come argomento a una funzione.
Anche se puoi passare una struttura come argomento, passare
un puntatore è più efficiente.
Sappiamo che è più efficiente. Ne abbiamo parlato in passato di come
il passaggio di elementi importanti possa essere più efficiente.
In questo caso, è ancora più
efficiente perché se stai passando un'intera struttura, se la struttura
è grande e contiene molti elementi, stai
copiando tutti gli elementi nella struttura in quel parametro.
Se passi un puntatore copiando semplicemente l'indirizzo.
Quindi è ancora più importante con
i puntatori come argomenti strutturati perché le strutture possono essere grandi.
Molte rappresentazioni di dati utilizzano strutture, contenenti puntatori ad
altre strutture.
Lo vedrai spesso in tutte le strutture dei
tuoi programmi c che sono puntatori e per i motivi che abbiamo menzionato.
Il fatto che puoi passarli come parametri nelle versioni precedenti da vedere
ed è anche più efficiente perché facciamo tutto per
passaggio per valore, il che è molto più
efficiente per copiare un indirizzo che
copiare tutti gli elementi di questa grande struttura.
Quindi dichiarare una struttura come un puntatore è piuttosto semplice.
Quando si definisce una variabile come un puntatore a colpire, è sufficiente utilizzare quell'asterisco dopo
il tipo di dati.
E abbiamo detto che le strutture sono davvero un nuovo tipo.
In questo caso, la nostra struttura data sempre importante.
Diciamo solo struct date e quindi usiamo il puntatore della
data asterisco, e probabilmente vorrai inizializzarlo su null.
Perché ricorda, quando dici struct date pointer date struct date
date pointer, che non alloca alcuna memoria,
crea memoria per memorizzare il puntatore effettivo.
Non sta creando memoria per i dati al suo interno.
Quindi potresti assegnare
a questo punto di data o all'indirizzo di un'altra data della
struttura o potresti allocare una memoria per esso da qualche
altra parte o potresti anche inizializzarla
con alcuni dati in questo modo.
Quindi il puntatore della data variabile può essere assegnato proprio come gli
altri puntatori, è possibile impostarlo in modo che punti alla
data odierna con l'istruzione di assegnazione, quindi se abbiamo già una dichiarazione della data odierna.
Useremmo solo la e commerciale per ottenere l'indirizzo.
Quindi creiamo una normale struttura della data ed è la data odierna.
Quindi questo creerebbe memoria per i tre elementi.
Quindi creiamo il nostro puntatore e gli
assegniamo semplicemente l'indirizzo dell'altra data.
Abbastanza diretto. Lo abbiamo fatto con tipi di dati
regolari, non è diverso quando lo fai con le strutture.
È quindi possibile accedere indirettamente a uno qualsiasi dei membri della struttura dati a cui punta il puntatore della data.
Quindi avremmo bisogno di fare qualcosa del genere.
Se volessi accedere all'elemento della
struttura del puntatore del giorno, dovresti prima de-referenziarlo.
E quindi sarebbe probabilmente utile metterlo tra parentesi in modo che sia molto semplice.
Devi de-referenziare il puntatore della data per ottenere il valore
di ciò a cui punta
la data odierna, il valore dell'indirizzo della data odierna
a cui punta per impostare valori particolari.
Quindi dovresti de-referenziarlo e quindi
fare il punto giorno è uguale a 21 e questo assegnerebbe 21 al
puntatore del giorno.
Ora quanto sopra sta solo dicendo che qualunque sia la data odierna,
vogliamo accedere a quel valore e impostare il giorno su 21.
In questo caso, le parentesi sono obbligatorie perché
il punto ha una precedenza maggiore dell'asterisco.
Se non hai inserito le parentesi lì dentro, si incasina perché proverebbe a fare
il punto prima di ottenere effettivamente il puntatore al puntatore,
per puntarlo ai dati.
Quindi proverebbe ad accedere ai dati con essenzialmente un puntatore nullo.
E quindi de-referenziando ottieni il valore.
Ora, quando usiamo gli struct, i puntatori.
Se volessimo testare il valore dei mesi memorizzati nella struttura della data puntata dal puntatore della data,
potremmo fare qualcosa di simile.
De-referenziamo il puntatore, lo mettiamo tra parentesi, quindi ha la precedenza sul punto e quindi diciamo che
il mese del punto è uguale a 12.
È una sintassi perfettamente valida.
Ma è un po 'ingombrante.
Quindi c ha la sua sintassi, quindi non devi de-referenziare tutto
e metterlo tra parentesi per le strutture.
Quando vuoi accedere a ciò a cui punta la struttura, non
devi de-referenziarlo in questo modo.
C'è un operatore speciale che esiste in c per le strutture dei puntatori, ed
è il trattino maggiore di.
È un trattino seguito dal segno maggiore di.
E questo ti permette di non dover de-referenziare il puntatore.
Quindi, invece di fare stella x tra parentesi e poi punto
y, puoi semplicemente dire x trattino maggiore di y.
Poiché x è un puntatore, non devi fare riferimento ad esso e vuoi
accedere all'elemento all'interno di quella struttura qualunque cosa tu stia indicando.
Quindi vedrai questa sintassi dappertutto. Non vedrai le
parentesi e il riferimento d. È perfettamente valido, ma stai digitando più caratteri.
È molto più efficiente dire semplicemente x trattino maggiore di y.
Quindi la precedente istruzione if qui sopra invece di de-referenziare tra parentesi, tutto ciò che devi
fare è dire se il puntatore della data è maggiore del mese.
Ed è in qualche modo intuitivo perché il trattino più grande di sta per dire che punta a qualcosa.
E quindi il trattino della data è maggiore del mese.
E quindi il puntatore della data è un puntatore ed è per questo che usi il trattino maggiore di.
Ora, se quella fosse una struttura
nidificata, faresti il trattino più grande della struttura e poi dovresti
fare un punto dopo per accedere agli elementi della struttura nidificata.
Ma poiché il puntatore del giorno è un puntatore, devi usare il trattino uguale o
il trattino maggiore di.
Quindi ecco un esempio, stiamo creando la nostra data struct, abbiamo
tre elementi, quindi stiamo creando il nostro puntatore della data del puntatore e stiamo creando solo
una variabile regolare oggi. Assegniamo al nostro
puntatore l'indirizzo di oggi e poi assegniamo gli elementi
effettivi della struttura, usiamo semplicemente quel trattino maggiore di e poi possiamo stamparlo.
Quindi diamo un'occhiata a questo nei blocchi di codice per vedere come funziona.
Quindi metterò questo esempio qui.
E in genere quando si definiscono le strutture, le si
definisce al di fuori di una funzione, perché in tal caso è possibile utilizzarle in qualsiasi funzione.
È un po 'come un globale. Ma ricorda che
quando definisci la struttura non stai effettivamente creando alcun ricordo.
Quindi crei ancora le variabili come variabili locali o puntatori, ma la definizione
effettiva è al di fuori della funzione.
Quindi, se vuoi usarlo, puoi usarlo in qualsiasi funzione.
Quindi qui in alto, abbiamo creato una nuova struttura di tipo data o
nome fino ad oggi e contiene tre numeri interi, la terminiamo con un punto e virgola.
Creiamo quindi un paio di variabili all'interno del main.
Uno che è solo una normale data di struttura variabile oggi
e poi uno che è un puntatore. Sappiamo che è un puntatore perché ha l'asterisco prima di esso.
Sappiamo che un puntatore quando crei un puntatore crea solo alloca memoria per
memorizzare quell'indirizzo per quel valore di quel puntatore.
Quindi dovremmo effettivamente assegnargli un indirizzo che facciamo con la e commerciale oggi perché
questo in realtà crea un rapporto di memoria.
Quindi ora il puntatore della data punterà a qualunque cosa
oggi sia con l'indirizzo di oggi.
Possiamo quindi iniziare ad assegnargli dei dati usando questo trattino più che molto utile e
semplicemente assegnare un dato come chiunque altro usa semplicemente l'operatore di assegnazione.
E poi quando lo
stampiamo, noterai che non dobbiamo de-referenziarlo per stamparlo.
Per stamparlo, possiamo
semplicemente dire i tipi di dati per gli specificatori di formato qui percentuale più alta percentuale
d uguale per i numeri interi e quindi usiamo semplicemente il separatore di trattini
perché questo sta effettivamente facendo la de-referenziazione ricorda.
Ora potremmo cambiare questo e questo sarebbe valido.
Dobbiamo mettere tra parentesi e poi dobbiamo dire il punto del mese.
In entrambi i casi va bene, ma abituati al trattino quindi maggiore perché è una sintassi migliore.
E ora quando lo creiamo,
vedrai che stampa i valori dei puntatori della
data e sono tutti impostati su 9 25 e 2015.
Quindi dice che la data odierna è 9 25 15. È stato formattato per stampare in questo modo.
Roba piuttosto semplice.
Ricorda solo quel trattino maggiore di, questa è una nuova
sintassi per i puntatori a strutture.
Ma tutto il resto qui è normale.
Sappiamo che i puntatori devono avere indirizzi di memorizzazione,
quindi sappiamo che dobbiamo allocare la memoria dinamica o dobbiamo assegnargli una struttura esistente.
Torniamo indietro e parliamo di più di strutture e puntatori.
Parliamo ora di strutture che contengono puntatori.
Sappiamo come creare un puntatore per una struttura.
Potremmo allocare memoria per quel puntatore se non volessimo assegnare
un indirizzo esistente di un puntatore esistente.
Ma puoi anche avere elementi all'interno di una struttura
che è essa stessa un puntatore. Quindi un puntatore potrebbe anche essere un membro di una struttura.
Non dobbiamo usare quell'array di caratteri.
Quindi potremmo dire struct,
potremmo nominarlo in pointers e quindi potremmo usare qui in questo esempio abbiamo due
numeri interi, due puntatori interi p1 e p2, sintassi perfettamente valida per farlo.
Quindi una struttura chiamata nei puntatori è definita per contenere due puntatori interi.
Il primo si chiama p1 e il secondo si chiama p2.
Puoi definire una variabile di tipo struct nei puntatori proprio come
normal, struct, int pointers, pointers.
I puntatori alle variabili possono ora essere usati proprio come le altre strutture.
Pointers non è un puntatore.
Sono puntatori int. Contiene due elementi al suo interno che
sono puntatori, ma i puntatori in sé non sono un puntatore effettivo.
Quindi fai attenzione a questo. È una variabile di struttura che ha due puntatori
come membri, è diverso.
Quindi, se vogliamo accedere agli elementi all'interno della variabile
pointers che si trova in pointers, non
usiamo il trattino maggiore di perché pointers non è un puntatore, è una struttura.
Useremmo il punto per
accedere a p1 e accedere a p2.
Quindi, in questo esempio, creiamo la nostra
struttura, creiamo una variabile. Non è un puntatore chiamato puntatori.
Quindi creiamo due numeri interi, i1
uguale a 100 e i2 che non è inizializzato.
Ora p1 e p2 sono puntatori.
Quindi devono contenere indirizzi di memoria validi in cui gli indirizzi sono
stati allocati in memoria.
Ed è quello che stanno facendo in i1 e i2.
Quando si dichiarano le variabili intere, la memoria viene allocata per tali numeri interi.
Quindi, assegnando l'indirizzo i1 e i2 a p1 e p2, va benissimo perché i1
e i2 hanno memoria allocata perché sono numeri interi.
Ora, se i puntatori interi non fossero puntatori interi ma fossero puntatori a
caratteri, allora dovresti allocare memoria dinamica.
Quindi ora stiamo usando solo numeri interi, quindi assegniamo gli indirizzi dei due numeri interi a p1 e p2.
Ora possiamo dire puntatori a stella. p2 è uguale a 97,
negativo 97.
Perché dobbiamo de-referenziarlo lì perché
i pointer. p2 è ciò a cui ci riferiamo.
Puntatori. pt p2 è un puntatore.
Non stiamo accedendo a un elemento di una struttura.
Stiamo dereferenziando tutti i puntatori. p2.
E gli assegneremo il valore di qualunque cosa che punta a 97 negativo.
Quindi nota la differenza lì.
Non stiamo creando un puntatore alla struttura.
Stiamo creando un puntatore a variabile regolare, ma
contiene due elementi che sono puntatori.
Quindi, se vogliamo assegnare a questi due elementi all'interno della struttura, dobbiamo
de-referenziarli usando l'operatore di riferimento indiretto, ed è quello
che facciamo lì.
E poi nelle nostre due dichiarazioni di stampa.
Ancora una volta, vogliamo stampare i valori di ciò a cui punta
p1, quindi lo de-referenziamo lo stesso
con p2 e quindi vogliamo anche
stampare di nuovo i valori a cui puntano i puntatori.
Quindi nel primo stiamo dicendo che i1 è
uguale a percentuale I puntatori virgola. p1 è uguale a
percentuale I.
Quindi il primo tra virgolette doppie sta solo stampando quel
testo effettivo, non sta de-referenziando nulla dopo la virgola è dove stiamo facendo la de-referenziazione.
Prima stampiamo i1 che è 100 e poi
stampiamo i puntatori p1 che sarebbero 100.
I2 sarà effettivamente negativo
97 dopo averlo assegnato a p2.
Allora per quanto riguarda gli array di caratteri o i puntatori di caratteri. Nella lezione precedente abbiamo parlato di
come creare un array di caratteri come elemento all'interno
di un puntatore. Ma per quanto riguarda i puntatori
ai caratteri, dovremmo fare
qualcosa come il primo e l'ultimo con
array di caratteri o dovremmo semplicemente fare qualcosa come il primo e l'ultimo
con i puntatori ai caratteri come elementi. Qual è l'opzione migliore.
Bene, vedremo. È davvero di nuovo una cosa notazionale, ma ci sono vantaggi nell'usare i puntatori.
Quindi, quando creiamo puntatori a caratteri all'interno dei nostri elementi, di
solito è un po 'meglio.
Ma devi capire cosa sta succedendo qui quando crei
un elemento array char array come elemento o un puntatore come elemento, entrambi
sono validi. Ma c'è una grande differenza.
Quindi, se diciamo nomi di strutture
veep o vp e diciamo Talia e Summers e poi
diciamo nome puntatore a struttura. Quindi ricorda, i nomi delle strutture contengono gli array di caratteri.
I nomi dei puntatori Struct contengono i puntatori.
E qui stiamo inizializzando i puntatori uguali a Brad e Fallingjaw.
E poi li stampiamo, stampiamo il primo elemento, che è
un carattere a e vp e poi
il primo elemento di treas, che è un puntatore al primo.
Quindi la struttura nomina la variabile veep.
Quello che sta succedendo qui è che le stringhe sono immagazzinate all'interno della struttura.
La struttura ha allocato un totale di 40 byte per contenere i due nomi,
sono array di caratteri.
Questo è ciò che accade quando creiamo matrici di caratteri all'interno di un puntatore.
Stiamo allocando 40 byte per contenere questi due
nomi, 20 e 20.
Per il puntatore denomina la variabile treas se abbiamo due puntatori primo e ultimo, si tratta
di stringhe che vengono memorizzate ovunque il compilatore memorizzi le costanti di stringa.
La struttura contiene i due
indirizzi perché sono puntatori. I puntatori contengono, cosa, valori che sono indirizzi.
Quindi tutto ciò che farà è allocare due puntatori all'interno di quella struttura.
Sono 16 byte per la maggior parte dei sistemi, i puntatori di solito 8 byte o 4 byte.
La struttura del nome struct p non alloca spazio per memorizzare le
stringhe perché alloca solo spazio per i puntatori stessi.
I puntatori hanno valori che sono indirizzi.
L'indirizzo a cui punta non c'è memoria allocata per questo.
Quindi, quando crei quei due puntatori, non c'è memoria allocata per quei due puntatori.
Non assegna spazio.
Quindi può essere utilizzato solo con stringhe per cui è stato assegnato spazio altrove.
Ogni volta che hai puntatori all'interno di una struttura,
devi usare malloc o calloc.
In caso contrario, potrebbero essere archiviati ovunque siano archiviate le costanti stringa.
E questa è la grande differenza. Quindi i puntatori e la struttura p
name dovrebbero essere usati solo per gestire le stringhe che sono state create e allocate altrove nel programma.
Non si desidera assegnare dati
a quello a meno che non lo si sia allocato dinamicamente o che sia
stato allocato da qualche altra parte, forse
da una struttura esistente o qualcosa del genere. Ma in genere, vorrai mallocarli.
Di nuovo, molto diverso.
Stai allocando 40 byte per questi due array di caratteri all'interno della struttura del nome.
Per la struttura del nome p, hai due puntatori.
Non c'è memoria allocata per questo quando crei una variabile di quel tipo.
Sta solo creando memoria per i due puntatori di caratteri, che sono otto byte ciascuno.
Per allocare effettivamente la memoria per ciò a cui puntano il primo e l'ultimo, devi fare un malloc.
Quindi un caso in cui ha senso usare un puntatore e una struttura
è gestire le
stringhe è se stai allocando dinamicamente quella memoria.
Di solito è quando lo
usi perché altri tipi di dati consentiti assegneranno automaticamente la memoria per te.
Quindi si desidera utilizzare un puntatore per memorizzare l'indirizzo.
Questo ha il vantaggio che puoi chiedere a malloc di allocare solo la quantità di
spazio necessaria per una stringa. Abbiamo parlato dei vantaggi dell'allocazione dinamica della memoria.
Quando crei cose staticamente in fase di compilazione, creerai
solo un numero specifico di byte.
Cosa succede se hai bisogno di più byte, sei fottuto, il tuo programma non funzionerà.
Devi tornare indietro e ricompilare. Con le stringhe allocate dinamicamente,
è possibile riadattare, creare la quantità di memoria necessaria, il che lo
rende molto più efficiente nel programma.
Quindi utilizzerai malloc su qualsiasi puntatore creato all'interno di una struttura.
Quindi, in questo caso, hai una struttura chiamata name count.
Hai due puntatori, puntatori a caratteri fname e cognome.
E poi hai un int, lettere.
Le due stringhe qui dentro non sono memorizzate nella struttura.
Creiamo solo memoria per i puntatori, che contengono indirizzi.
Vogliamo creare memoria per fname e lname per ciò a cui puntano i puntatori.
Quindi vogliamo archiviarlo in un blocco gestito da una posizione di memoria dinamica, che utilizza malloc.
L'indirizzo delle due stringhe è ciò che è memorizzato, non ciò a cui stanno puntando.
Quindi gli indirizzi sono ciò con cui funzionano tipicamente le funzioni di gestione delle
stringhe, il che è positivo ed è per questo che vuoi usare
i puntatori lì dentro perché quindi non devi farlo, puoi semplicemente passarli molto più facilmente.
Quindi ecco un esempio. Abbiamo una funzione chiamata get info.
Ci vuole un puntatore.
E possiamo passare quel puntatore molto facilmente.
Ora creeremo anche un altro o
per il nostro array di puntatori
che ci viene passato, allocheremo memoria
per esso.
Quindi vedrai che creiamo un personaggio chiamato temp e
poi memorizziamo alcuni dati in quel temp.
Chiediamo all'utente di ottenere qualcosa. Non abbiamo familiarità con gli sgets, ma è lo stesso che viene salvato un po 'scanf.
Quindi allochiamo memoria per fname all'interno di quel puntatore e lo facciamo proprio come
allocare memoria per qualsiasi carattere. Chiamiamo malloc, passiamo
il numero di byte.
In questo caso, la lunghezza di str di 10 più 1 per il terminatore nullo.
E poi ricorda, malloc trasforma una star del vuoto, quindi l'abbiamo scelto per una star del personaggio.
Ma questo è fondamentale proprio qui. Questa chiamata malloc è necessaria per tutte le strutture che contengono puntatori.
Ora abbiamo passato questo puntatore e al puntatore non è mai stata allocata
una memoria.
Quindi stiamo allocando la memoria qui dentro. Possiamo quindi fare la nostra copia molto facilmente.
Possiamo copiare temp in fname della nostra struttura pst che è stata
passata come parametro.
E poi possiamo fargli inserire il loro cognome, possiamo mallocare più memoria per
il cognome e quindi memorizzarlo nella variabile struct del cognome.
Questo è ciò che dovrai fare quando avrai puntatori a personaggi
all'interno delle tue strutture, devi fare il malloc.
Perché ancora una volta, quei puntatori memorizzano solo l'indirizzo.
Assegnano memoria sufficiente per memorizzare un indirizzo, memoria
insufficiente per memorizzare una stringa.
Quindi qualunque sia l'indirizzo a cui punta, devi allocare memoria per quello e
lo fai come al solito con
il malloc, l'unica differenza è che qui usi il trattino maggiore del
segno per accedere all'elemento effettivo all'interno di quella struttura, che è un puntatore a carattere,
quindi puoi assegnarlo e questo sta facendo la de-referenziazione.
Quindi vorrai giocare con questa roba. Questo è molto comune
per i puntatori a caratteri all'interno degli struct.
Avremo una sfida in cui dovrai farlo. Ma gioca con questo codice, entra
nei blocchi di codice, inizia ad allocare memoria per stringhe o
puntatori di caratteri all'interno di strutture e vedi come
puoi quindi usare la copia di str per mettere effettivamente i dati all'interno, ma
devi prima allocare la memoria prima di assegnare .
Se stai facendo costanti come nell'esempio precedente, chissà dove è allocata quella memoria.
Quindi, ancora una volta, molti concetti importanti presentati in questa conferenza.
Questa idea di una struttura che è un puntatore in
questa nuova sintassi
in cui è possibile utilizzare il trattino quindi maggiore di anziché utilizzare
l'operatore di riferimento indiretto, è molto carina.
E poi effettivamente avere elementi come puntatori e come allocare
la memoria per i puntatori ai caratteri. Grazie.