Dinamiskā atmiņas piešķiršana c. C valodas vadības konstrukcijas. Programmu kā funkciju attēlošana. Darbs ar atmiņu. Struktūras. Standarta dinamiskās atmiņas piešķiršanas funkcijas

Pirms iedziļināties objektorientētā izstrādē, mums ir jāveic īsa atkāpe par darbu ar atmiņu C++ programmā. Mēs nevarēsim uzrakstīt nevienu sarežģītu programmu, ja izpildes laikā nevarēsim piešķirt atmiņu un tai piekļūt.
Programmā C++ objektus var piešķirt statiski kompilēšanas laikā vai dinamiski izpildes laikā, izsaucot funkcijas no standarta bibliotēkas. Galvenā atšķirība šo metožu izmantošanā ir to efektivitāte un elastība. Statiskā piešķiršana ir efektīvāka, jo atmiņas piešķiršana notiek pirms programmas izpildes, taču tā ir daudz mazāk elastīga, jo mums iepriekš jāzina piešķirtā objekta veids un lielums. Piemēram, nepavisam nav viegli ievietot teksta faila saturu statiskā virkņu masīvā: mums iepriekš jāzina tā lielums. Uzdevumiem, kuriem nepieciešams uzglabāt un apstrādāt nezināmu skaitu elementu, parasti ir nepieciešama dinamiska atmiņas piešķiršana.
Līdz šim visos mūsu piemēros ir izmantota statiskā atmiņas piešķiršana.

Teiksim, definējot mainīgo ival

Int ival = 1024;
liek kompilatoram piešķirt apgabalu atmiņā, kas ir pietiekami liels, lai saglabātu mainīgo tipa int, saistīt nosaukumu ival ar šo apgabalu un ievietot tur vērtību 1024. Tas viss tiek darīts kompilācijas stadijā, pirms programma tiek izpildīta.

Ar ival objektu ir saistītas divas vērtības: mainīgā faktiskā vērtība, šajā gadījumā 1024, un atmiņas apgabala adrese, kurā šī vērtība tiek saglabāta.

Mēs varam atsaukties uz vienu no šiem diviem daudzumiem. Kad mēs rakstām:
Int ival2 = ival + 1;

tad mēs piekļūstam ival mainīgajā ietvertajai vērtībai: pievienojam tam 1 un inicializējam ival2 mainīgo ar šo jauno vērtību 1025. Kā mēs varam piekļūt adresei, kurā atrodas mainīgais?

Ir arī īpaša adreses uzņemšanas darbība, ko apzīmē ar simbolu &. Tā rezultāts ir objekta adrese. Šis paziņojums pintes rādītājam piešķir mainīgā ival adresi:

Int *pinte; pint = // pinte iegūst ival adreses vērtību

Mēs varam piekļūt objektam, kura adrese satur pint (mūsu gadījumā ival), izmantojot operāciju atsauces atcelšana, sauc arī netiešā adresācija. Šī darbība ir apzīmēta ar simbolu *. Lūk, kā netieši pievienot vienu ival, izmantojot tā adresi:

*pinte = *pinte + 1; // netieši palielina ival

Šis izteiciens darbojas tieši tāpat kā

Ival = ival + 1; // izteikti palielina ival

Šim piemēram nav nekādas jēgas: rādītāja izmantošana, lai netieši manipulētu ar ival mainīgo, ir mazāk efektīva un mazāk skaidra. Mēs esam snieguši šo piemēru tikai tāpēc, lai sniegtu ļoti vienkāršu priekšstatu par rādītājiem.
Patiesībā norādes visbiežāk tiek izmantotas, lai manipulētu ar dinamiski piešķirtiem objektiem.

  • Galvenās atšķirības starp statisko un dinamisko atmiņas piešķiršanu ir šādas:
  • statiskie objekti tiek attēloti ar nosauktiem mainīgajiem, un darbības ar šiem objektiem tiek veiktas tieši, izmantojot to nosaukumus. Dinamiskajiem objektiem nav īpašvārdu, un darbības ar tiem tiek veiktas netieši, izmantojot norādes;

Kompilators automātiski piešķir un atbrīvo atmiņu statiskiem objektiem. Programmētājam pašam par to nav jāuztraucas. Atmiņas piešķiršana un atbrīvošana dinamiskiem objektiem ir pilnībā programmētāja atbildība. Tas ir diezgan sarežģīts uzdevums, un to risinot ir viegli pieļaut kļūdas. Operatori new un delete tiek izmantoti, lai manipulētu ar dinamiski piešķirto atmiņu.

Jaunajam operatoram ir divas formas. Pirmā forma piešķir atmiņu vienam noteikta veida objektam:

Int *pinte = jauns int(1024);
Šeit jaunais operators piešķir atmiņu nenosauktam objektam tipa int, inicializē to ar vērtību 1024 un atgriež izveidotā objekta adresi. Šī adrese tiek izmantota, lai inicializētu pintes rādītāju. Visas darbības ar šādu nenosauktu objektu tiek veiktas, atceļot atsauci uz šo rādītāju, jo Nav iespējams skaidri manipulēt ar dinamisku objektu.

Jaunā operatora otrā forma piešķir atmiņu noteikta izmēra masīvam, kas sastāv no noteikta veida elementiem:

Šajā piemērā atmiņa ir piešķirta četru int elementu masīvam.
Diemžēl šī jaunā operatora forma neļauj inicializēt masīva elementus.
Neskaidrību rada tas, ka abas jaunā operatora formas atgriež vienu un to pašu rādītāju, mūsu piemērā tas ir rādītājs uz veselu skaitli. Gan pint, gan pia ir deklarēti tieši tādi paši, bet pint norāda uz vienu int objektu, bet pia norāda uz četru int objektu masīva pirmo elementu.

Kad dinamisks objekts vairs nav vajadzīgs, mums ir skaidri jāatbrīvo tam piešķirtā atmiņa. Tas tiek darīts, izmantojot dzēšanas operatoru, kuram, tāpat kā jaunam, ir divas formas - vienam objektam un masīvam:

// viena objekta atbrīvošana delete pint; // masīva atbrīvošana delete pia; Kas notiek, ja aizmirstam atbrīvot piešķirto atmiņu? Atmiņa tiks izniekota, tā būs neizmantota, bet to nevar atgriezt sistēmā, jo mums nav uz to rādītāja. Šī parādība ir ieguvusi īpašu nosaukumu atmiņas noplūde
. Galu galā programma avarēs atmiņas trūkuma dēļ (ja tā, protams, darbojas pietiekami ilgi). Nelielu noplūdi var būt grūti noteikt, taču ir utilītas, kas var palīdzēt to izdarīt.

Mūsu saīsinātais pārskats par dinamisko atmiņas piešķiršanu un rādītāju izmantošanu, iespējams, radīja vairāk jautājumu, nekā tas atbildēja. 8.4. sadaļā ir detalizēti aplūkoti saistītie jautājumi. Tomēr mēs nevarējām iztikt bez šīs novirzes, jo Array klase, kuru mēs izstrādāsim nākamajās sadaļās, ir balstīta uz dinamiski piešķirtas atmiņas izmantošanu.

2.3. uzdevums

Izskaidrojiet atšķirību starp četriem objektiem:

a) int ival = 1024; (b) int *pi = (c) int *pi2 = new int(1024); d) int *pi3 = jauns int;

2.4. uzdevums

Ko dara tālāk norādītais koda fragments? Kāda ir loģiskā kļūda? (Ņemiet vērā, ka indeksa() darbība ir pareizi piemērota pia rādītājam. Šā fakta skaidrojumu var atrast 3.9.2. sadaļā.)
Int *pi = jauns int(10); int *pia = jauns int;< 10) {
kamēr (* pi
pia[*pi] = *pi;

*pi = *pi + 1;

) izdzēst pi; dzēst pia;

Tātad. Trešais veids, kas mums šajā tēmā ir visinteresantākais, ir dinamiskais atmiņas veids. < stdio.h> Kā mēs iepriekš strādājām ar masīviem? int a Kā mēs tagad strādājam? Mēs piešķiram tik daudz, cik nepieciešams: < stdlib.h> #iekļauts #iekļauts int main() (size_t izmērs;// Izveidojiet rādītāju uz int // – būtībā tukšs masīvs. int *saraksts; scanf( // un mūsu "tukšais masīvs" tagad attiecas uz šo atmiņu. saraksts = (int *)malloc(izmērs * sizeof(int));< size; ++i) { scanf (for (int i = 0 ; i < size; ++i) { printf (for (int i = 0 ; i"%d" , *(saraksts + i));) // *

// Neaizmirsti sakopt aiz sevis!

bezmaksas(saraksts); )

Void * malloc(izmērs_t izmērs);

Bet kopumā šī funkcija piešķir uninicializētas atmiņas izmēra baitus (nevis nulles, bet atkritumus).

Tātad. Trešais veids, kas mums šajā tēmā ir visinteresantākais, ir dinamiskais atmiņas veids. < stdio.h> Kā mēs iepriekš strādājām ar masīviem? int a Kā mēs tagad strādājam? Mēs piešķiram tik daudz, cik nepieciešams: < stdlib.h> Ja piešķiršana bija veiksmīga, tiek atgriezts rādītājs uz piešķirtās atmiņas pirmo baitu. // – būtībā tukšs masīvs. Ja neizdodas - NULL. Arī errno būs vienāds ar ENOMEM (šo brīnišķīgo mainīgo apskatīsim vēlāk). Tas ir, pareizāk būtu rakstīt:< size; ++i) { scanf (for (int i = 0 ; i int main () ( izmērs_t izmērs; int *saraksts; scanf (< size; ++i) { printf (for (int i = 0 ; i, &size); // *

saraksts = (int *)malloc(size * sizeof(int));

Tātad. Trešais veids, kas mums šajā tēmā ir visinteresantākais, ir dinamiskais atmiņas veids. < stdlib.h> if (saraksts == NULL ) ( goto error; ) for (int i = 0 ; i

, saraksts + i);

) for (int i = 0 ; i

    , *(saraksts + i));

    ) bezmaksas(saraksts);

    atgriezties 0 ; kļūda: atgriešanās 1 ; )

    Nav nepieciešams notīrīt NULL rādītāju

    int main() (bezmaksas (NULL);)

    – tajā pašā klakšķēšanā viss noritēs labi (nekas netiks darīts), bet eksotiskākos gadījumos tas var arī sagraut programmu.

    Blakus malloc un free in mana varat redzēt arī:

    void * calloc(size_t count, size_t size);

    Tāpat kā malloc piešķirs atmiņu baitu lieluma objektiem. Piešķirtā atmiņa tiek inicializēta ar nullēm.

    void * realloc (void *ptr, size_t size);

Pārdala (ja var) atmiņu, uz kuru norāda ptr, pēc lieluma baitiem. Ja nepietiek vietas, lai palielinātu piešķirto atmiņu, uz kuru norāda ptr , realloc izveido jaunu sadalījumu (piešķiršanu), kopē vecos datus, uz kuriem norāda ptr , atbrīvo veco sadalījumu un atgriež rādītāju piešķirtajā atmiņā.

Statiskā atmiņas piešķiršana tiek veikta visiem globālajiem un lokālajiem mainīgajiem, kuriem programmā ir skaidras deklarācijas (neizmantojot rādītājus). Šajā gadījumā atmiņas piešķiršanas mehānismu nosaka mainīgā apraksta atrašanās vieta programmā un atmiņas klases specifikators aprakstā. Mainīgā veids nosaka piešķirtās atmiņas apgabala lielumu, bet atmiņas piešķiršanas mehānisms nav atkarīgs no veida. Ir divi galvenie statiskās atmiņas piešķiršanas mehānismi.

· Atmiņa katram globālajam un statiskajam (deklarētam ar statisko specifikāciju) mainīgajiem tiek piešķirta pirms programmas izpildes sākuma saskaņā ar tipa aprakstu. No programmas izpildes sākuma līdz beigām šie mainīgie ir saistīti ar tiem atvēlēto atmiņas apgabalu. Tādējādi tiem ir globāls kalpošanas laiks, taču to darbības joma ir atšķirīga.

· Vietējiem mainīgajiem, kas deklarēti blokā un bez specifikācijas statisks atmiņa tiek piešķirta citā veidā. Pirms programmas izpildes (kad tā ir ielādēta) tiek atvēlēts diezgan liels atmiņas apgabals, t.s. kaudze(dažreiz tiek lietoti termini programmu kaudze vai zvanu kaudze lai nošķirtu steku kā abstraktu datu tipu). Steka lielums ir atkarīgs no izstrādes vides, piemēram, MS Visual C++, pēc noklusējuma stekam tiek atvēlēts 1 megabaits (šo vērtību var pielāgot). Programmas izpildes laikā, ievadot noteiktu bloku, stekā tiek atvēlēta atmiņa blokā lokalizētajiem mainīgajiem (saskaņā ar to veida aprakstu, izejot no bloka, šī atmiņa tiek atbrīvota). Šie procesi tiek veikti automātiski, tāpēc C++ valodā bieži tiek izsaukti lokālie mainīgie automātiski.

Kad funkcija tiek izsaukta, stekā tiek atvēlēta atmiņa tās lokālajiem mainīgajiem, parametriem (parametra vērtība vai adrese tiek ievietota stekā), funkcijas rezultātam un saglabājot atgriešanās punktu - adresi programmā, kur jums ir jāatgriežas, kad funkcija ir pabeigta. Kad funkcija tiek aizvērta, visi ar to saistītie dati tiek noņemti no steka.

Termina “steka” lietojumu ir viegli izskaidrot – ar pieņemto pieeju atmiņas piešķiršanai un atdalīšanai no tās vispirms tiek noņemti mainīgie, kas stekā tiek novietoti pēdējie (tie ir mainīgie, kas lokalizēti visdziļākajā ligzdotajā blokā). Tas ir, atmiņas piešķiršana un atbrīvošana notiek pēc LIFO principa (LAST IN – FIRST OUT, last in – first out). Šis ir kaudze darbības princips. Mēs apskatīsim steku kā dinamisku datu struktūru un tās iespējamo ieviešanu nākamajā sadaļā.



Daudzos gadījumos statiski piešķirtā atmiņa noved pie tās neefektīvas izmantošanas (tas jo īpaši attiecas uz lieliem masīviem), jo statiski piešķirtā atmiņas apgabals ne vienmēr ir faktiski aizpildīts ar datiem. Tāpēc C++, tāpat kā daudzās valodās, ir ērti līdzekļi mainīgo dinamiskai ģenerēšanai. Dinamiskās atmiņas piešķiršanas būtība ir tāda, ka atmiņa tiek piešķirta (uztverta) pēc pieprasījuma no programmas un arī atbrīvota pēc pieprasījuma. Šajā gadījumā atmiņas lielumu var noteikt pēc mainīgā veida vai skaidri norādīt pieprasījumā. Tādus mainīgos sauc dinamisks. Spēja izveidot un izmantot dinamiskos mainīgos ir cieši saistīta ar rādītāja mehānismu.

Apkopojot visu iepriekš minēto, varam iedomāties šādu atmiņas sadales shēmu programmas izpildes laikā (2.1. attēls). Apgabalu izvietojums viena pret otru attēlā ir diezgan patvaļīgs, jo Operētājsistēma rūpējas par atmiņas piešķiršanas detaļām.

2.1.attēls – atmiņas sadalījuma diagramma

Noslēdzot šo sadaļu, pieskarsimies vienai sāpīgai problēmai, strādājot ar steku - tā pārplūdes iespējamībai (šo ārkārtas situāciju parasti sauc Stack Overflow). Iemesls, kas izraisīja problēmu, ir skaidrs - ierobežotais atmiņas apjoms, kas tiek atvēlēts stekam, ielādējot programmu. Visticamākās steka pārpildes situācijas ir lieli lokālie masīvi un rekursīvo funkciju izsaukumu dziļa ligzdošana (parasti tas notiek, ja rekursīvo funkciju programmēšana ir neprecīza, piemēram, tiek aizmirsta kāda termināļa filiāle).



Lai labāk izprastu steka pārpildes problēmu, iesakām veikt šo vienkāršo eksperimentu. Funkcijā galvenais deklarēt veselu skaitļu masīvu ar, teiksim, miljona elementu lielumu. Programma tiks apkopota, bet, to palaižot, radīsies steka pārpildes kļūda. Tagad pievienojiet precizētāju masīva apraksta sākumam statisks(vai izņemiet masīva deklarāciju no funkcijas galvenais) – programma darbosies!

Tajā nav nekā brīnumaina - vienkārši tagad masīvs nav novietots uz kaudzes, bet gan globālo un statisko mainīgo apgabalā. Atmiņas lielumu šim apgabalam nosaka kompilators - ja programma kompilēja, tad tā darbosies.

Tomēr, kā likums, programmā nav nepieciešams deklarēt statiski ģenerētus milzīgu izmēru masīvus. Vairumā gadījumu dinamiska atmiņas piešķiršana šādiem datiem būs efektīvāks un elastīgāks veids.

Dinamiskā un statiskā atmiņas piešķiršana. Priekšrocības un trūkumi. Atmiņas piešķiršana atsevišķiem mainīgajiem, izmantojot jaunos un dzēšanas operatorus. Iespējamas kritiskas situācijas, piešķirot atmiņu. Inicializācija, kad tiek piešķirta atmiņa

1. Dinamiskā un statiskā (fiksētā) atmiņas piešķiršana. Galvenās atšķirības

Lai strādātu ar informācijas masīviem, programmām šiem masīviem ir jāpiešķir atmiņa. Lai piešķirtu atmiņu mainīgo masīviem, tiek izmantoti atbilstoši operatori, funkcijas utt. Programmēšanas valodā C++ izšķir šādas atmiņas piešķiršanas metodes.

1. Statisks (fiksēts) atmiņas piešķiršana. Šajā gadījumā atmiņa tiek piešķirta tikai vienu reizi kompilēšanas laikā. Piešķirtās atmiņas apjoms ir fiksēts un nemainīgs līdz programmas izpildes beigām. Šādas atlases piemērs varētu būt 10 veselu skaitļu masīva deklarēšana:

int M; // atmiņa masīvam tiek piešķirta vienreiz, atmiņas lielums ir fiksēts

2. Dinamisks atmiņas piešķiršana. Šajā gadījumā tiek izmantota jauno un dzēšanas operatoru kombinācija. Jaunais operators piešķir atmiņu mainīgajam (masīvam) īpašā atmiņas apgabalā, ko sauc par kaudzi. Dzēšanas operators atbrīvo piešķirto atmiņu. Katram jaunam operatoram ir jābūt savam dzēšanas operatoram.

2. Dinamiskās un statiskās atmiņas piešķiršanas metožu izmantošanas priekšrocības un trūkumi

Dinamiskā atmiņas piešķiršana nodrošina šādas priekšrocības salīdzinājumā ar statisko atmiņas piešķiršanu:

  • atmiņa pēc vajadzības tiek piešķirta programmatiski;
  • Nav nevajadzīgas neizmantotās atmiņas izšķiešanas. Tiek piešķirts tik daudz atmiņas, cik nepieciešams un ja nepieciešams;
  • jūs varat piešķirt atmiņu informācijas masīviem, kuru lielums acīmredzami nav zināms. Masīva lielums tiek noteikts programmas izpildes laikā;
  • Ir ērti pārdalīt atmiņu. Citiem vārdiem sakot, tam pašam masīvam ir ērti piešķirt jaunu fragmentu, ja nepieciešams piešķirt papildu atmiņu vai atbrīvot nevajadzīgu atmiņu;
  • Izmantojot statiskās atmiņas piešķiršanas metodi, ir grūti pārdalīt atmiņu masīva mainīgajam, jo ​​tas jau ir fiksēts. Dinamiskās atlases metodes gadījumā tas tiek darīts vienkārši un ērti.

Statiskās atmiņas piešķiršanas metodes priekšrocības:

  • statisko (fiksēto) atmiņas piešķiršanu vislabāk izmantot, ja ir zināms informācijas masīva lielums un tas paliek nemainīgs visas programmas izpildes laikā;
  • statiskajai atmiņas piešķiršanai nav nepieciešamas papildu atdalīšanas darbības, izmantojot dzēšanas operatoru. Tā rezultātā tiek samazināts programmēšanas kļūdu skaits. Katram jaunam operatoram ir jābūt savam dzēšanas operatoram;
  • programmas koda, kas darbojas ar statiskiem masīviem, prezentācijas dabiskums (dabiskums).

Atkarībā no veicamā uzdevuma programmētājam jāspēj pareizi noteikt, kura atmiņas piešķiršanas metode ir piemērota konkrētam mainīgajam (masīvam).

3. Kā piešķirt atmiņu, izmantojot jauno operatoru vienam mainīgajam? Vispārējā forma.

Viena mainīgā lieluma atmiņas piešķiršanas vispārējā forma, izmantojot jauno operatoru, ir šāda:

ptrName= jauns tips;
  • ptrName– mainīgā (rādītāja) nosaukums, kas norādīs uz piešķirto atmiņu;
  • veids- mainīgs tips. Atmiņas apjoms ir piešķirts pietiekami, lai tajā ievietotu šāda veida mainīgā lieluma vērtību. veids .
4. Kā atbrīvot atmiņu, kas piešķirta vienam mainīgajam, izmantojot dzēšanas operatoru? Vispārējā forma

Ja atmiņa mainīgajam tiek piešķirta, izmantojot jauno operatoru, tad pēc mainīgā izmantošanas pabeigšanas šī atmiņa ir jāatbrīvo, izmantojot dzēšanas operatoru. C++ valodā tas ir priekšnoteikums. Ja neatbrīvosiet atmiņu, atmiņa paliks piešķirta (aizņemta), taču neviena programma to nevarēs izmantot. Šajā gadījumā tas notiks "atmiņas noplūde" (atmiņas noplūde).

Java un C# programmēšanas valodās pēc piešķiršanas nav nepieciešams atbrīvot atmiņu. To dara “atkritumu savācējs”.

Viena mainīgā dzēšanas operatora vispārīgā forma ir šāda:

dzēst ptrName;

Kur ptrName– rādītāja nosaukums, kuram iepriekš tika piešķirta atmiņa, izmantojot jauno operatoru. Pēc dzēšanas operatora izpildes rādītājs ptrName norāda uz patvaļīgu atmiņas apgabalu, kas nav rezervēts (piešķirts).

5. Piemēri atmiņas piešķiršanai (jaunai) un atbrīvošanai (dzēšanai) pamattipu rādītājiem

Piemēri parāda jauno un dzēšanas operatoru izmantošanu. Piemēri ir vienkāršoti.

1. piemērs. Rādītājs, lai ierakstītu int . Vienkāršākais piemērs

// atmiņas piešķiršana, izmantojot jauno operatoru int * p; // rādītājs uz int p = jauns int ; // piešķirt atmiņu rādītājam*p = 25; // ierakstīt vērtības atmiņā // rādītājam atvēlētās atmiņas izmantošana int d; d = *p; // d = 25 // atbrīvot rādītājam atvēlēto atmiņu - obligāti dzēst p;

2. piemērs. Rādītājs, lai rakstītu dubultā

// piešķirt atmiņu rādītājam, lai to dubultotu dubultā * pd = NULL; pd = jauns dubultnieks; // piešķirt atmiņu if (pd!=NULL ) ( *pd = 10,89; // rakstiet vērtības dubultā d = *pd; // d = 10,89 - lietojiet programmā // brīva atmiņa izdzēst pd; )
6. Kas ir “atmiņas noplūde”?

« Atmiņas noplūde" - tas ir tad, kad jaunais operators piešķir atmiņu mainīgajam, un programmas beigās to neatbrīvo dzēšanas operators. Šajā gadījumā sistēmas atmiņa paliek aizņemta, lai gan vairs nav vajadzības to izmantot, jo programma, kas to izmantoja, jau sen ir pabeigusi savu darbu.

“Atmiņas noplūde” ir tipiska programmētāja kļūda. Ja “atmiņas noplūde” atkārtojas daudzas reizes, iespējams, ka visa pieejamā atmiņa datorā tiks “aizņemta”. Tas radīs neparedzamas operētājsistēmas sekas.

7. Kā piešķirt atmiņu, izmantojot jauno operatoru, pārtverot kritisku situāciju, kurā atmiņa var netikt piešķirta? Bad_alloc izņēmums. Piemērs

Izmantojot jauno operatoru, iespējams, ka atmiņa netiks piešķirta. Atmiņa var netikt piešķirta šādos gadījumos:

  • ja nav brīvas atmiņas;
  • brīvās atmiņas apjoms ir mazāks par jaunajā operatorā norādīto.

Šajā gadījumā tiek izmests bad_alloc izņēmums. Programma var pārtvert šo situāciju un attiecīgi rīkoties.

Piemērs. Piemērā ir ņemta vērā situācija, kad jaunais operators var nepiešķirt atmiņu. Šajā gadījumā tiek mēģināts piešķirt atmiņu. Ja mēģinājums ir veiksmīgs, programma turpinās. Ja mēģinājums neizdodas, funkcija iziet ar kodu -1.

int main() ( // deklarēt peldošu rādītāju masīvu peldēt * ptrArray; mēģināt (// mēģiniet piešķirt atmiņu 10 peldošajiem elementiem<< << endl; cout << ba.what() << endl; return -1; ptrArray = jauns pludiņš ; } ) noķert (bad_alloc ba) ( cout// iziet no funkcijas< 10; i++) ptrArray[i] = i * i + 3; int d = ptrArray; cout << d << endl; delete ptrArray; // ja viss ir kārtībā, tad izmantojiet masīvu for (int i = 0; i
// masīvam piešķirta brīvā atmiņa

Jaunais atmiņas piešķiršanas operators vienam mainīgajam ļauj veikt vienlaicīgu inicializāciju ar šī mainīgā vērtību.

Kopumā atmiņas piešķiršana mainīgajam ar vienlaicīgu inicializāciju izskatās šādi

ptrName= jauns veids( vērtību)
  • ptrName– rādītāja mainīgā nosaukums, kuram piešķirta atmiņa;
  • veids– veids, uz kuru norāda rādītājs ptrName ;
  • vērtību– vērtība, kas iestatīta piešķirtajam atmiņas apgabalam (rādītāja vērtība).

Piemērs. Atmiņas piešķiršana mainīgajiem lielumiem ar vienlaicīgu inicializāciju. Tālāk ir norādīta galvenā () funkcija konsoles lietojumprogrammai. Demonstrēta atmiņas piešķiršana ar vienlaicīgu inicializāciju. Tas ņem vērā arī situāciju, kad mēģinājums piešķirt atmiņu neizdodas (kritiskā situācija bad_alloc).

#include "stdafx.h" #include izmantojot namespace std; int main() ( // atmiņas piešķiršana ar vienlaicīgu inicializāciju peldēt * pF; int * pI; char * dators;<< mēģināt ( << endl; cout << ba.what() << endl; return -1; ptrArray = jauns pludiņš ; } // mēģiniet piešķirt atmiņu mainīgajiem ar vienlaicīgu inicializēšanu pF = jauns pludiņš (3,88); // *pF = 3,88 pI = jauns int (250); // *pI = 250 pC = jauna ogle ("M" ); // *pC = "M" ) noķert (bad_alloc ba) ( cout "Izņēmums: atmiņa nav piešķirta"// ja atmiņa ir atvēlēta, tad izmantojiet norādes pF, pI, pC<< "*pF = " << f<< endl; cout << "*pI = " << i << endl; cout << "*pC = " << c << endl; peldēt f = *pF; // f = 3,88 int i = *pI; // i = 250; char c;

c = *pC; // c = "M"

// drukāt inicializētās vērtības

cout

// brīva atmiņa, kas iepriekš piešķirta rādītājiem

dzēst pF;

dzēst pI;

izdzēst datoru;

atgriezties 0; )

Dinamiskā atmiņas piešķiršana

Norādes ir grūti pārvaldīt. Ir diezgan vienkārši rādītājā ierakstīt nepareizu vērtību, kas var izraisīt grūti atveidojamu kļūdu. Piemēram, jūs nejauši mainījāt atmiņā esošā rādītāja adresi vai nepareizi atvēlējāt atmiņu informācijai, un tad jūs var sagaidīt pārsteigums: tiks pārrakstīts vēl viens ļoti svarīgs mainīgais, kas tiek izmantots tikai programmas iekšienē. Šajā gadījumā jums būs grūti reproducēt kļūdu. Nebūs viegli saprast, kur tieši ir kļūda. Un ne vienmēr būs triviāli to novērst (dažkārt nāksies pārrakstīt ievērojamu programmas daļu).

Lai atrisinātu dažas problēmas, ir aizsardzības un apdrošināšanas metodes:

Izpētot norādes C valodā, mēs atklājām dinamiskās atmiņas piešķiršanas iespējas. Ko tas nozīmē? Tas nozīmē, ka ar dinamisko atmiņas piešķiršanu atmiņa tiek rezervēta nevis kompilācijas, bet gan programmas izpildes stadijā. Un tas dod mums iespēju efektīvāk piešķirt atmiņu, galvenokārt masīviem. Izmantojot dinamisko atmiņas piešķiršanu, mums nav nepieciešams iepriekš iestatīt masīva lielumu, jo īpaši tāpēc, ka ne vienmēr ir zināms, kāda izmēra masīvam vajadzētu būt. Tālāk apskatīsim, kā var piešķirt atmiņu.

Funkcija malloc() ir definēta stdlib.h galvenes failā, to izmanto, lai inicializētu norādes ar nepieciešamo atmiņas apjomu. Atmiņa tiek piešķirta no RAM sektora, kas pieejams visām programmām, kas darbojas mašīnā. Funkcijas malloc() arguments ir jāpiešķir atmiņas baitu skaits, kas atgriež rādītāju uz piešķirto bloku atmiņā. Funkcija malloc () darbojas tāpat kā jebkura cita funkcija, nekas jauns.

Tā kā dažādiem datu tipiem ir dažādas atmiņas prasības, mums kaut kā jāiemācās iegūt baitu lielumu dažādiem datu tipiem. Piemēram, mums ir nepieciešama atmiņas sadaļa int tipa vērtību masīvam — tas ir viens atmiņas lielums, un, ja mums ir jāpiešķir atmiņa tāda paša izmēra masīvam, bet tipa char — tas ir dažāda izmēra. Tāpēc jums kaut kā jāaprēķina atmiņas apjoms. To var izdarīt, izmantojot operāciju sizeof(), kas ņem izteiksmi un atgriež tās lielumu. Piemēram, sizeof(int) atgriezīs baitu skaitu, kas nepieciešams, lai saglabātu int vērtību. Apskatīsim piemēru:


Yandex.Direct


#iekļauts int *ptrVar = malloc(sizeof(int));

Šajā piemērā in 3. rinda PtrVar rādītājam tiek piešķirta adrese atmiņas vietai, kuras izmērs atbilst int datu tipam. Automātiski šī atmiņas apgabals kļūst nepieejams citām programmām. Tas nozīmē, ka pēc tam, kad piešķirtā atmiņa kļūst nevajadzīga, tā ir skaidri jāatbrīvo. Ja atmiņa nav skaidri atbrīvota, tad pēc programmas pabeigšanas atmiņa operētājsistēmai netiks atbrīvota, to sauc par atmiņas noplūdi. Varat arī noteikt piešķirtās atmiņas lielumu, kas jāpiešķir, nododot nulles rādītāju. Šeit ir piemērs:



Kā redzat, šim apzīmējumam ir viens ļoti spēcīgs punkts, mums nevajadzētu izsaukt funkciju malloc(), izmantojot sizeof(float). Tā vietā mēs nodevām rādītāju uz pludiņa tipu malloc(), un tādā gadījumā piešķirtās atmiņas lielums automātiski noteiks sevi!

Tas ir īpaši noderīgi, ja jums ir nepieciešams piešķirt atmiņu tālu no rādītāja definīcijas:


peldēt *ptrVar;

/* .