[Mūzika spēlē ]
DŪGS LOJS: Tātad šajā videoklipā mēs runāsim par to, kā dinamiski
piešķirt atmiņu C.
Tagad, kā atruna, ja neesat skatījies mūsu video par norādēm vai
vispār neesat pazīstams ar norādes jēdzienu, jūs, iespējams,
vēlēsities to apskatīt pirms šī.
Šis video, dinamiskā atmiņas piešķiršana visparīgā nozīmē , darbojas
īpaši ar rādītājiem.
Tāpēc, pirms iedziļināties šajā konkrētajā videoklipā, vēlaties
pārliecināties, vai esat apmierināts ar to.
Sāksim bez turpmākas piepūles.
Tagad mēs jau esam redzējuši, kā strādāt ar norādēm.
Bet parasti mēs to darījām tikai kontekstā, lai norādītu
norādesmainīgo, kuru mēs statiski deklarējam citam jau esošam
mainīgajam.
Iespējams, atceraties no mūsu video par rādītājiem, ka mums bija tāda
līnija kā int x ir vienāds ar 5.
Un tad mēs teiktu, ka int zvaigznīte px ir vienāds ar & x.
Bet tas ir vienīgais veids, kā mēs esam redzējuši, kā strādāt ar
norādēm.
Un tā būtībā ir statiskās norādes lietošana.
Lai strādātu ar norādēmstatiski, mums precīzi jāzina, cik daudz
atmiņas mēs izmantosim programmas kompilēšanas brīdī.
Tāpēc mēs neveidosim nekādu atmiņu darba gaitā
Visa atmiņa, ko mēs izmantosim, jau ir iestatīta, kad mēs sākam.
Tas rada sava veida problēmu, vai ne?
Ko darīt, ja mēs nezinām, cik daudz atmiņas mums būs, kad sākam darbu?
Varbūt jūs lūgsit lietotājam norādīt jums numuru, un viņš ģenerēs
simts saistīto saraksta objektu.
Tātad viņi radīs simtiem kaut ko, un jūs to nezināt.
Vai varbūt viņi ģenerē 200 vai 400.
Mēs nevarētu to iepriekš paredzēt, tāpēc mums ir jāizmanto dinamiskā
atmiņas piešķiršana, lai programmā iegūtu jaunu atmiņu, kamēr
programma jau darbojas.
Un tas ir nedaudz savādāk nekā jebkas, ko esam redzējuši iepriekš.
Mēs to varam izdarīt, piešķirot atmiņu no kaut kā, kas tiek saukts
heap - kaudzīte.
Kaudzīte ir milzīgs atmiņu krājums.
Un līdz šim vienīgais mums pazīstamais atmiņas kopums tiek saukts par
kaudzi .
Bet dinamiski piešķirtā atmiņa nāk no kaudzītes , un statiski
piešķirtā atmiņa — jebkas, kam parasti piešķirat nosaukumu, piemēram,
mainīgā nosaukums — tiks iestatīta kaudzē.
Un viss, ko izveidojat dinamiski, kamēr programma darbojas, nāks no
kaudzītes .
Bet, kā redzams no šīs diagrammas, kaudze un kaudzīte patiesībā ir
viena un tā pati liela atmiņas daļa.
Tas ir tā, ka kaudze, ko mēs piešķiram no apakšas uz augšu, parasti
ir, lai to vizualizētu.
Tādējādi kaudze atmiņas adreses būs nedaudz mazākas, un kaudzītes
skaitļi būs lielāki, un tas tiks piešķirts lejup.
Tātad tas patiešām ir viens milzīgs atmiņas kopums, taču mēs to
saucam par divām dažādām lietām atkarībā no tā, kā tā tiek izmantota.
Statiski deklarētās atmiņas kaudze palielinās un dinamiski piešķirtās
atmiņas kaudzīte samazinās.
Kā mēs tiekam pie šīs dinamiski piešķirtās atmiņas?
Kā mēs varam piekļūt kaudzītes atmiņai?
Mums ir jāizmanto jauna funkcija, ko sauc par malloc.
Malloc var iegūt par mārciņu, ieskaitot standarta lib.h, stdlib.h.
Un vienīgais arguments, kas jums ir jānodod malloc, ir tas, cik baitu
atmiņas vēlaties.
Tātad, ja vēlaties veselu skaitli, sakiet malloc 4.
Ja vēlaties rakstzīmi , malloc 1.
Ja vēlaties dubultot, malloc 8 utt.
Un jūs varat rīkoties daudz sarežģītāk, kā mēs drīz redzēsim.
Malloc mēģinās atrast kaudzītē tādu atmiņas apjomu , kādu jūs
prasījāt.
Tātad tas mēģinās atrast, piemēram, astoņus blakus esošos atmiņas
baitus.
Ja mēs piešķiram dubultu, tas mēģinās atrast astoņus blakus esošos
atmiņas baitus no kaudzītes .
Un tas, ko tas darīs, ir tas, ka tas atgriezīs jums norādi uz šo
atmiņu.
Tātad vienīgais veids, kā mēs varēsim piekļūt dinamiski piešķirtajai
atmiņai vai to izmantot, ir novirzot no malloc iegūto norādi.
Un tāpēc pirms došanās uz priekšu ir svarīgi saprast norādes.
Tagad ir iespējams, ka malloc faktiski nevarēs atjaunot atmiņu, un
tādā gadījumā tas atgriezīsies pie nulles.
Un tāpēc viens no pirmajiem noteikumiem, kas jāatceras par dinamiski
piešķirto atmiņu, ir vienmēr pārbaudīt nulli pēc malloc.
Tagad, kāpēc tas varētu notikt?
Varbūt jums ir beigusies atmiņa.
Kaudze un kaudzīte ir tikko pilnībā beigušās, vai arī ir notikusi
kāda katastrofāla kļūme, ko nevaram paredzēt.
Bet jebkurā gadījumā, ja no mūsu norādes video atceraties, ka norādes
tiek atceltas uz nulles rādītāju, tās ir sliktas ziņas.
Tāpēc pirmā lieta, ko vēlaties darīt — protams, pēc malloc — ir
jāpārbauda, vai jūs nesaņēmāt nulli.
Un, ja jūs to izdarījāt, jums būs jāpārtrauc programma, jo kaut kas
ir nogājis greizi un jūs nevarat turpināt to, kas jums ir pašlaik.
Tātad, ja mēs vēlamies vienkārši iegūt veselu skaitli, statiski
deklarējiet to, mēs varam vienkārši pateikt int x.
Tādējādi kaudzē tiks izveidots mainīgais ar nosaukumu x, kuram pēc
tam varēsim piešķirt jebkuru vērtību, kas mums patīk.
Ja vēlaties dinamiski piešķirt veselu skaitli, mēs sakām: int star px
ir vienāds ar malloc 4.
Un iemesls, kāpēc mēs šeit sakām četrus, ir tāpēc, ka veselā skaitlī
ir četri baiti.
Es viegli būtu varējis izmantot arī operatora izmēru, kas ir pieejams
C valodā.
Būtībā jūs to nododat tipam — tāpēc tas ir nedaudz savādāk.
Parasti ar funkcijām tiek ievadīts mainīgais.
Izmantojot malloc, jūs ievadāt vai ar sizeof, drīzāk ievadāt tipu, un
tas atgriezīs, cik baitu šis tips aizņem sistēmā.
Tātad int zvaigznīte px ir vienāds ar malloc lielums int vai int
zvaigznīte ps ir vienāds ar malloc 4 būtībā nozīmē, ka malloc
gatavojas atrast jums četrus baitus atmiņas, kas atrodas blakus viens
otram uz kaudzītes .
Un malloc atgriezīs jums norādiuz šo atmiņu, ko sauc par px.
Un tad mēs varētu neizmantot šo norādi, kā mēs redzējām norādes
video, lai ar to manipulētu, ievietotu tajā vērtību un darītu ar to
visu, ko vēlamies.
Apskatīsim vēl vienu piemēru.
Varbūt mēs vēlamies iegūt veselu skaitli no lietotāja.
Tāpēc atcerieties, ka CS50 mums ir funkcija get_int, ko varam
izmantot.
Int x ir vienāds ar get_int.
Mēs būtībā prasām lietotājam kādu veselu skaitļu vērtību.
Un tas mums dos kādu skaitli, cerams, šajā piemērā pozitīvu skaitli.
Pieņemsim, ka es vēlos paziņot par masīvu, kurā ir tik daudz peldošu
elementu.
Es to varu izdarīt, pasakot float stack_array un pēc tam
kvadrātiekavās — kas atkal norāda masīva lielumu — x.
Tas ir likumīgi C 99 un C 11.
Ja izmantojat ļoti vecu C versiju, tas faktiski nebija atļauts.
Bet jūs varat to izdarīt.
Būtībā mēs varam iegūt mainīga izmēra masīvu kaudzē , ko mēs šeit
darām, jo mēs iepriekš nezinām, cik liels tas būs.
Mēs to tikai saņemam no lietotāja.
Bet šādā veidā mēs varētu deklarēt pludiņu masīvu kaudzē, kur vienumu
skaits šajā masīvā ir numurs, ko lietotājs mums tikko iedeva.
Un es varu arī dinamiski piešķirt pludiņu masīvu kaudzītē — nevis
kaudzē , atcerieties, jo šoreiz mēs dinamiski piešķiram atmiņu.
Float star heap_array.
Tā ir mana norāde uz atmiņu, ko es iegūstu.
Un es gribu malloc x reizes lielāku par peldošu vērtību.
Tātad, ja es vēlos iegūt 50 pludiņus, man vajag 50 reizes lielāku par
peldošu vērtību, tātad 50 reizes 4.
Varbūt es gribu 100, tāpēc tas būtu 100 reiz četri.
Tāpēc mēs tur veicam reizināšanu.
Un malloc atgriežmums vienu milzīgu šāda izmēra atmiņas bloku, ko mēs
pēc tam varam vienkārši uzskatīt par jebkuru citu masīvu.
Tomēr mums ir jācīnās ar dinamiski piešķirto atmiņu, un tas ir kaut
kas tāds, ko mēs vēl neesam redzējuši, un tas ir, ka tā netiek
atgriezta atpakaļ sistēmā, kad esat pabeidzis.
Tātad, ja vēl neesat redzējis mūsu videoklipu par izsaukumā kaudzēm
vai kaudzes rāmjiem, iespējams, tas jums vēl nav zināms.
Bet parasti, kad funkcija beidz darboties, visa atmiņa, kas tika
izveidota šīs funkcijas pastāvēšanai , tiek iznīcināta un tiek
atbrīvota atpakaļ sistēmā, lai to izmantotu kaut kur citur.
Tātad cits funkcijas izsaukums, kas varētu parādīties, var izmantot
to pašu atmiņas bitu iepriekš.
Tas ir diezgan ērti, vai ne?
Šī sistēma pastāvīgi tiek pārstrādāta, kur vien iespējams.
Bet, kad jūs sakāt sistēmai, es gribu atmiņas bloku, sistēma neko par
to neuzņemsies.
Tā nepieņems, ka nav redzējusi, ka jūs ar to izsaucāt50 līniju garumā
vai citādi un tas tikai dinamiski atbrīvos to vai atgriezīs to
atpakaļ sistēmā.
Un, ja neizdodas atgriezt pietiekami daudz atmiņas sistēmā, tas tiek
saukts par atmiņas noplūdi.
Un atmiņas noplūdes patiešām nav labas, jo tās patiešām var palēnināt
jūsu sistēmu.
Un patiesībā ir dažas pārlūkprogrammas, kas paliks bez nosaukuma un
ir savā ziņā bēdīgi slavenas ar to, ka neatbrīvo atmiņu vai neatbrīvo
atmiņu atpakaļ sistēmā, kad tas ir paveikts.
Un tas patiešām var izraisīt jūsu datora palēnināšanos, jo šī
pārlūkprogramma pamatā aizņem daudz atmiņas, un citas jūsu datorā
esošās programmas nevar to izmantot, jo pārlūkprogramma to ir
piešķīrusi un teikusi : nē, šī ir mana.
Man to vajag.
Taču tā nekad to neizlaiž atpakaļ, lai to varētu koplietot sistēmā.
Tāpēc, ja pārlūkprogrammu atstājat atvērtu vairākas dienas vai dažas
pārlūkprogrammas ir atvērtas vairākas dienas vai nedēļas, varat
pamanīt, ka datora darbība sāk palēnināties.
Tagad noteikums ir tāds, kā jau minēju iepriekš, kad esat pabeidzis
darbu ar dinamiski piešķirto atmiņu, viss, kas jums jādara, ir to
atbrīvot.
Un freeir vēl viena funkcija, kas ir pieejama standarta lib.h.
Un būtībā tas, ko norādījāt uz free, ir norādeuz jebkuru atmiņu, kuru
iepriekš esat dinamiski piešķīrusi.
Un būtībā tas ir tikai paziņojums sistēmai, es esmu pabeidzis.
Varat izmantot šo atmiņu jebkuram nolūkam.
Tātad, šeit ir vēl viens piemērs, pieņemsim, ka mēs vēlamies izveidot
50 rakstzīmju garu masīvu — tātad būtībā lielu virkni.
Mēs to sauksim par vārdu.
Mēs to dinamiski sadalām.
Tad mēs ar to darām dažas lietas.
Varbūt mēs bijām kādas funkcijas vidū.
Ziniet, mēs mainām tā vērtību.
Mēs piešķiram jaunas rakstzīmes .
Mēs, piemēram, to izdrukājam.
Un tad mēs nolemjam, ka esam to pabeiguši.
Kā to atgriezt sistēmā?
Mums tas vienkārši jāatbrīvo.
Tātad mēs atbrīvojām word.
Word ir norādeuz šo atmiņas bloku.
Tā ir lieta, ko mēs vēlamies atbrīvot.
Tātad malloc un free ir trīs pamatnoteikumi, kurus ir patiešām
svarīgi internalizēt.
Pirmkārt, viss, ko izmantojat malloc, ir jāatbrīvo, pirms programma
beidz darboties.
Tagad, protams, ja jūsu programma faktiski beidz darboties, kad tā ir
pilnībā pabeigta, piemēram, kad esat atgriezis nulli no main, tā tiks
atbrīvota.
Bet tā ir laba prakse, lai pārliecinātos, ka viss, ko piešķirat jums,
jūs atvrīvojāt , skaidri izsaucot free funkciju, nevis tikai
paļauties uz to, ka sistēma to izdarīs, kad programma ir pilnībā
pabeigta.
Tātad viss, kad jūs izmantojat malloc, jums ir jāatbrīvo.
Otrais noteikums — tikai tās lietas, kuras jūs darāt, izmantojot
malloc ir tās, kuras jums vajadzētu atbrīvot.
Tātad, ja jūs statiski deklarējāt mainīgo, jūs nevēlaties to atbrīvot.
Sistēma parūpēsies par visas statiski deklarētās atmiņas atbrīvošanu
jūsu vietā.
Tā ir tikai dinamiski piešķirta atmiņa — tikai atmiņa, kad jūs
izmantojat malloc , ir lietas, kuras jums vajadzētu atbrīvot.
Un pēc tam noteikti neatbrīvojiet vienu un to pašu atmiņas bloku
vairāk nekā vienu reizi, jo tas var radīt dubultas atbrīvošanas
problēmu, un jums ir sava veida apgriezta atmiņas noplūde, piemēram,
atmiņas plūsma vai kaut kas tamlīdzīgs, kad jūs mānījāt sistēmu,
domājot, ka jums ir vairāk atmiņas nekā jums patiesībā ir.
Tas nav tāds, ko jūs patiesībā vēlaties darīt.
Tāpēc atcerieties, ka viss, kas jums jādara, izmantojot malloc ir
jāatbrīvo.
Tikai tās lietas, ko jūs darāt, izmantojot malloc, jums vajadzētu
atbrīvot.
Un neatbrīvojiet neko vairāk kā vienu reizi.
Noslēdzam šo videoklipu, apskatot pāris koda rindiņas un vizuālu
piemēru, lai parādītu, kas tieši notiek, kad to darām.
Tātad vispirms statiski deklarēsim veselu skaitli, ko sauc par m.
Un tāpēc tas izskatītos apmēram šādi.
Esmu nokrāsojis lodziņu zaļu, jo manā galvā veseli skaitļi vienmēr ir
zaļos lodziņos .
Un uz lodziņa esmu uzrakstījis marķējumu m.
Tad es teikšu int zvaigznīte a.
Tāpēc šeit es statiski deklarēju norādi, ko sauc par a.
Tāpēc atcerieties, ka man patīk domāt, ka tā ir int zvaigznīte ,
tātad tā ir int līdzīga, tāpēc tā būs zaļa.
Tas ir zaļgans lodziņš. .
Tas nav zaļš lodziņš, jo tas ir paredzēts ints.
Bet iekšējās zvaigznītes ir sava veida zaļas.
Tās attiecas uz ints.
Tātad šie ir citi lodziņi , un šo sauc par a.
Tad es varu teikt, ka int star b ir vienāds ar malloc sizeof int.
Tātad, kas šeit notiks?
Nu, man būs kaut kas, ko sauc par b, kam es dodu vārdu.
Tātad tas būs gaiši zaļgans lodziņš.
Un es lūdzu malloc dinamiski piešķirt man vienu int vērtu vietu.
Un tas, kas patiesībā notika vizuāli, ir šāds.
Ņemiet vērā, ka veselajam skaitlim es nenosaucu nosaukumu.
Man tam nav vārda.
Tas ir viens no veidiem, kā parasti es zinu, ka man nav statiski
deklarēta vesela skaitļa.
Vienīgais veids, kā es varu atsaukties uz šo zaļo lodziņu, ir atsaukt
rādītāju uz b.
Tātad b ir nosaukums, un tas ir statiski deklarēts.
Bet es neizmantoju atsauci b, lai piekļūtu šai dinamiski piešķirtajai
atmiņai.
Un, protams, es to šeit nedarīšu.
Bet pirmais, kas man jādara tūlīt pēc tam, ir pārbaudīt, vai b nav
nulle.
Ja b vērtība ir nulle, kaut kas ir nogājis greizi un man nekavējoties
jāapturprogramma.
Tālāk es varu teikt kaut ko līdzīgu: a ir vienāds ar & m.
Ja atceraties no mūsu video par norādēm, būtībā tas nozīmē tikai
punktus uz m.
Tātad šeit tiek izveidota šī bultiņa.
Tālāk pieņemsim, ka a ir vienāds ar b.
Pirms es jums parādīšu, kas šeit notiks, vai varat mēģināt nedaudz
uzminēt, kas tas būs?
Apskatiet diagrammu.
Padomājiet par to, kā attiecības varētu mainīties.
Ja es saku a ir vienāds ar b, ko es saku?
Es saku, ka a un b tagad norāda uz vienu un to pašu vietu, jo īpaši,
a virzīsies uz punktu, uz kuru pašlaik norāda b.
Tā vietā, lai norādītu uz m, a tagad norādīs šeit uz šo dinamiski
piešķirto bloku, uz kuru es šobrīd varu tikai atsaukties — tagad varu
uz to atsaukties vairākos veidos.
Tagad es varētu teikt zvaigznīte a vai zvaigznīte b.
Es varu uz to atsaukties abos gadījumos, jo abi tie paši statiski
deklarētie mainīgie norāda uz vienu un to pašu lokāciju.
Tad es varu teikt, ka m ir 10.
Tā nav problēma, jo 10 tika deklarēti statiski.
Tas ir lodziņš, par kuru es jau zinu.
Tam ir nosaukums.
Tātad, kad es saku, ka m ir vienāds ar 10, es tikai saku, ka
ievietojiet 10 šajā zaļajā lodziņā ar nosaukumu m.
Zvaigznīte b ir vienāda ar m plus 2.
Tas ir nedaudz dīvaini, vai ne?
Bet kas ir m plus 2?
Nu, m ir 10, tātad 10 plus 2 ir 12.
Un zvaigznīte b ir vienāda ar 12.
Kad es saku zvaigznīte b, es atceļu atsauci uz b.
Tāpēc es dodos pa bultiņu uz vietu, uz kuru b norāda.
Un es tajā vietā ievietošu 12.
Labi?
Tālāk es dodos uz brīvu b.
Tāpēc atcerieties, kas notiek ar free, ka mēs sakām sistēmai, ka
vairs nestrādājam ar šo.
Esam pabeiguši.
Varat ņemt šos četrus baitus atpakaļ, vai arī neatkarīgi, kāds ir tā
lielums.
Tas ir četri baiti.
Varat to ņemt atpakaļ un izmantot jebkurā citā programmā.
Tātad, kad es atbrīvoju b, šī atmiņa būtībā pazūd.
a un b joprojām norāda uz vietu, kur tas bija agrāk, bet man tas
vairs nav pieejams.
Ja es mēģināšu tai pieskarties, sistēma var tikt apdraudēta
Piemēram, ja es mēģinātu pateikt šo — zvaigznīte a ir 11 — kas
notiks?
Neprognozējama uzvedība.
Mēs nezinām.
Mēs varam iegūt segmentācijas kļūdu, jo mēs pieskaramies atmiņai,
kuras vairs nav — mums vairs nav rakstīšanas tiesību.
Jūs, iespējams, varat izvairīties no šīs problēmas, atkarībā no tā,
kur sistēmā šī atmiņa atradās.
Bet tas ir neparedzami.
Tātad, kad esat atbrīvojis atmiņu, atcerieties, ka jūs sakāt
sistēmai, ka man tā vairs nav vajadzīga.
Tātad, ja jūs mēģināt to izmantot, var notikt dažas dīvainas lietas.
Pie dinamiskās atmiņas piešķiršanas ir nedaudz dīvaini pierast, jo
mēs esam ļoti pieraduši pie idejas izveidot mainīgo un piešķirt tam
vērtību, un vienkārši manipulēt ar to. Tāpēc izmantojot norādes, lai
kontrolētu atmiņas atrašanās vietu un ar norādēm tās netieši mainītu,
var būt nedaudz dīvaini.
Bet tikai turpiniet vingrināties ar to.
Iekļaujiet šos noteikumus par to, kā veiksmīgi veikt malloc un
atbrīvot, lai neizraisītu atmiņas noplūdes vai kaut ko tamlīdzīgu.
Un jums vajadzētu veiksmīgi uzsākt darbu.
Es esmu Dags Loids.
Šis ir CS50.