[Powered by Google Translate] [Näiturid]
[Rob Bowden] [Harvardi Ülikool]
[See on CS50] [CS50.TV]
>> Räägime suunanäitajaks.
Siiani oleme alati just nimetatud asju mälus selgelt nimepidi.
Me oleme öelnud int n = 42; ja siis kui tahame kasutada muutuja n,
me lihtsalt nimetame seda nime me anname seda kirjutades midagi n * 2.
Aga see muutuja peavad elama kuskil mälus.
Kui soovite kasutada väärtust, mis on praegu talletatud n,
või uuendage väärtust, et n hoiab,
Teie programm peab teadma, kus mälu otsima n.
Kui mälu muutuv elu on kutsunud oma aadress.
See on nagu maja aadress.
Oskan leida kellegi maja nii kaua, kui ma tean oma kodune aadress,
ja arvutiprogrammi leida muutuja, kui ta teab oma mälu aadressi.
Mida vihjeid anda on viis otse suheldes nende mälu aadresse.
>> Palju võimu C saadakse on võimalik manipuleerida mälu niimoodi.
Aga suure võimuga kaasneb suur vastutus.
Näiturid saab kasutada ohtlikult piisavalt, et palju programmeerimiskeeli
peida vihjeid täielikult.
Niisiis, miks C annab meile lähtekohad siis?
Nagu te teate, argumente C funktsioon
alati kopeeritakse parameetrid funktsioon.
Niisiis, kutsudes midagi swap mõned muutujad x ja y
ei saa vahetada väärtused x ja y helistades funktsioon,
isegi kui see võib olla mugav.
Nagu näeme hiljem, ümberkirjutamise swap võtta viiteid asukohad vajavad vahetasid
võimaldab tal mõjutada oma helistaja muutujad.
>> Vaatame läbi kõndida suhteliselt lihtne näide sellest, mida vihjeid teha.
Oletame, et meil on int n = 4; ja int * pointer_to_n = & n.
Vau! Natuke uus süntaks katmiseks.
Esiteks, ärgem seda tõlgendada & n.
Pea meeles, et kõik mälus on mingi aadress.
Ampersand nimetatakse "aadress" operaator.
Niisiis, & n viitab aadress mälus, kus n on salvestatud.
Nüüd oleme ladustamiseks sellele aadressile uues muutuja, pointer_to_n.
Mis tüüpi on selle uue muutuja?
Tärn on muutuja tüüp,
ja me lugeda tüübiks int *.
Int * tähendab, et pointer_to_n on muutuja, mis salvestab aadress täisarv.
Me teame, et & n on int * kuna n on täisarv, ja me võtame aadress n.
Int * on näide osutiloendurid.
Niipea kui hakkate nägema tärnid on tüüp,
sa tead, et sul tegu on suunanäitajaks.
Just nagu me võib kuulutada muutuja kui int x ja char y,
võib öelda, int * z ja char * w.
Int * ja char * on vaid uut tüüpi meie jaoks kasutada.
Asukoht * ei lähe kuhugi enne muutuja nimi.
Nii, nii int * pointer_to_n - koos * kõrval keskmine, nagu meil siin -
ja int * pointer_to_n koos * kõrval pointer_to_n kehtivad.
Aga siin, ma panna * kõrval int.
See ei ole tähtis, mis sulle meeldib, lihtsalt olla järjekindel.
>> Joonistame skeem selle eest.
Me kõigepealt muutuja n, mis me teha nii väikest kasti mälu.
Selle näite, oletame, et see kast asub aadressil 100.
Toas see ruut, me ladustamiseks väärtus 4.
Nüüd on meil uus muutuja, pointer_to_n.
Tal on oma kasti mälu,
mis me ütleme on aadressil 200.
Toas see ruut, oleme ladustamiseks aadress n,
mis me enne ütlesin oli 100.
Korduma skeemidel, näete seda näidatakse sõnasõnaline nool
jättes pointer_to_n kasti osutades kast, mis salvestab n.
Nüüd, mida me saame tegelikult teha pointer_to_n?
Noh, kui me ütleme midagi sellist * pointer_to_n = 8, see on erinev kasutus, tärniga
see on täiesti eraldi kasutada tärniga deklareerimisel muutuja
kohta osutiloendurid.
Siin tärniga nimetatakse dereference operaator.
Meie diagramm, mis * pointer_to_n = 8 vahenditega on
minna sisaldav kast pointer_to_n, järgige noolt
ja siis määrata kasti lõpus nool väärtus 8.
See tähendab, et pärast seda joont, kui me püüame kasutada n see on väärtus 8.
Sõna "osuti" kasutatakse palju eri kontekstides.
Siin püüan olla järjekindel.
Osutiloendurid on midagi int *.
Selles videos osuti kasutatakse vaid tähendab väärtus osutiloendurid,
nagu pointer_to_n mis on tüüpi int *.
Kõikjal me harjunud lihtsalt öelda, n, saame nüüd selle asemel öelda * pointer_to_n,
ja kõik töötab sama hästi.
>> Kõnni läbi teise lihtsa näite.
Oletame, et meil on int n = 14; int * pointer = &n; n + + ja (* pointer) + +.
Esimene rida loob uue kasti mälu märgistatud n.
Seekord me ei märgistama kasti selgesõnalise aadress, kuid see on veel üks.
Kasti sisemus, me ladustamiseks number 14.
Järgmisel real loob teise kasti osuti.
Ja seest see kast, me talletamiseks viit kasti n.
Niisiis, oletame, joonistada noole kursor n.
Nüüd, n + + kaupa väärtuse väljal n,
nii me läheme 14-15.
Lõpuks (* pointer) + + läheb kasti Kursori
dereferences kasti väärtus, mis tähendab Järgige noolt, kus ta märgib,
ja kaupa salvestatud väärtus olemas, nii et me läheme 15-16.
Ja ongi kõik.
N nüüd salvestab number 16 pärast seda, kui suurendatakse kaks korda -
kui otse, kasutades muutuja nimi n ja teine ​​läbi pointer_to_n.
>> Kiire viktoriin. Mis sa arvad, see tähendab, et kui ma üritan öelda midagi && n?
Noh, selle ümber kirjutada kui & (& n), mis saavutatakse sama asi.
(& N) tagastab aadress muutuja n mällu.
Aga siis siis välimine ampersand üritab naasta aadress aadress.
See on nagu üritab teha & 2.
See ei ole mõtet saada aadress vaid mõned arv
kuna see ei ole mälus.
Kasutades kahte ampersandid järjest ei ole kunagi õige mõte.
Aga nüüd, mida see tähendab, kui ma üritan öelda int ** double_pointer = & pointer?
Nüüd ma loon uue kasti double_pointer,
ja sees, et kast Ma ladustamiseks aadress pointer,
mis tähendab, et ma joonistada noole double_pointer kasti osuti kasti.
Teade tüüpi double_pointer, int **.
N oli täisarv, osuti salvestatud aadress n, ja nii see on tüüpi int *.
Nüüd double_pointer salvestab aadress pointer, nii et see on tüüpi int **.
>> Niisiis, mida me arvame, see tähendab -
** Double_pointer = 23?
Pange tähele, et ma olen nüüd viite mahavõtmine kaks korda.
Jälgi kasti-ja nool skeemil oleme juba loonud.
Esiteks, me läheme kasti double_pointer.
Esimene * tähendab Järgige noolt korraga.
Nüüd oleme väljal kursor.
Teine täht ütleb Järgige noolt uuesti,
ja nüüd me oleme kasti n, ja seadsime väärtus selle lahtri 23.
Pange tähele, et dereference ja "aadress" operaatorid on pöördvõrdelised üksteist.
See võimaldab mul teha midagi sellist * & * & n = 42.
Kuigi see töötab, sa ei tohi kunagi midagi sellist teha praktikas.
Mida me tegelikult siin teed?
Esimene märk haarab aadress muutuja n.
Siis on meil dereference operaator, mis tähendab, et me ei kavatse sellele aadressile mälu,
nii et me oleme tagasi n.
Nüüd haarata aadress n jälle ja kohe dereference,
nii et me oleme tagasi n ja pood 42.
Niisiis, iga paari * ja lihtsalt tühjaks.
>> On olemas spetsiaalne osuti nimetatakse nullviit.
See on kursor, et me ei tohiks kunagi dereference.
Selline osuti on oluline, sest see annab meile viis eristada
pointer, mis peaks ja ei tohiks dereferenced.
Kui üritate dereference nullviida,
tavaliselt oma programmi krahhi koos killustatust süü,
mis olete näinud.
Niisiis, oletame, et meil on kood int * x = null; * x = 4.
Selles näites, see võib tunduda ilmselge, et me teeme midagi halba,
kuid pea meeles, et null võiks tõesti olla raha tagastatud kõne funktsioon
nagu malloc, kui malloc pole suuteline eraldama mälu kutsuma kasutaja.
Seetõttu, kui selle asemel seadsime x väärtus alates kõne malloc,
nagu int * x = malloc (sizeof (int)), siis peaksime alati selgelt näha
et näha, kas null tagastati.
Kui (x == null) / / uhoh! tagasipöördumist; muidu saame jätkata ja öelda * x = 4.
>> Nii et jällegi, miks peaks me kunagi kasutada viiteid?
Vaatame näiteks programm, kus me peame kasutama viiteid -
lihtne swap funktsiooni.
Oletame, et mul on kaks täisarvu, int x = 4; ja int y = 15;
ja ma tahan kirjutada funktsioon nimega swap, et saan kasutada nii: swap (x, y).
Pärast seda joont, väärtused sees muutuja x peaks olema 15,
ja väärtus sees muutuja y peaks olema 4.
Väärtused sees x ja y on vahetatud.
Ilma suunanäitajaks, me võiksime proovida midagi tühine swap (int, int b);
int tmp = b, b =; = tmp.
Aga, kas te märkate sellega probleem?
Pea meeles, et salvestatud väärtuse muutuja on lihtsalt koopia x väärtus,
ja väärtus b kopeeritakse y.
Tehtud muudatusi ja b ei kajastu x ja y.
Seega, kui väärtused a ja b on korrektselt vahetatud,
x ja y ei ole muutunud üldse.
Nüüd saab muuta swap funktsiooni nii, et selle argumentideks
on viiteid, et muutujad tuleb vahetada, näiteks nii:
void swap (int *, int * b); int tmp = * b; * b = *; * = tmp.
Pea meeles, et vahetustehingud argumendid on nüüd suunanäitajaks,
ja seega peame läbima aadress x ja y kõne vahetada, näiteks nii:
swap (& x, & y).
See nüüd õigesti vahetuslepingud väärtused x ja y.
>> Joonistame kasti-ja nool skeemil näha, miks see töötab.
Alustame meie kaks karpi mälestuseks, x ja y.
Kasti sisemus x on number 4 ja sisemust jaoks y meil on 15.
Nüüd, sees kõne swap funktsiooni, meil on veel kaks kasti
jaoks argumendid a ja b;
osutab kasti x, ja b võrra kasti y.
Uus kast on loodud muutuja tmp,
ja sees on meil salvestada tulemus viite mahavõtmine b,
mis tähendab "Järgige noolt alates kast b."
Niisiis, me Kauplusse 15 sees tmp.
Siis me järgime nool b ja salvestada siit tulemus viite mahavõtmine,
mis on väärtus 4.
Lõpuks me järgime noolt ja kauplus milline on hetkel sees tmp, mis on 15.
Pange tähele, et ruudud x ja y on õigesti vahetasid väärtused.
>> Kui me õpime paremini tundma malloc ja dünaamiline mälu haldamine,
me näeme, et meil pole muud valikut kui kasutada viiteid.
Jalutuskäik läbi kasti-ja nool skeemil ühegi programmi
aitab teil aru saada, mida programm tegelikult teeb.
>> Minu nimi on Rob Bowden, ja see on CS50.
[CS50.TV]
>> See on erinev kasutamiseks Tärn - Bleah, ma vihkan seda sõna.
Kõikjal me harjunud lihtsalt öelda, n, saame nüüd öelda pointer_to_n - no sa ei saa - * pointer_to_n.