Kontakty

C Výber pamäte pre reťazec. Dynamické pridelenie pamäte. Ako prideliť pamäť nového operátora s odpočúvaním kritickej situácie, v ktorej pamäť nemusí vyniknúť? Výnimočná situácia bad_alloc. Príklad

Práca s dynamickou pamäťou je často úzkeho miesta v mnohých algoritmoch, ak nie je aplikovaním špeciálnych trikov.

V článku budem zvážiť pár takýchto techník. Príklady v článku sa líšia (napríklad z), pretože preťaženie nových a odstraňovačov sa používa a vďaka tomu sa syntaxové konštrukcie budú minimálne, a remake programu je jednoduché. Tiež opísané podvodné kamene, ktoré sa nachádzajú v procese (samozrejme, guru, ktorý čítal štandard z kôry na šupku, nebude prekvapený).

0. Potrebujeme manuálnu pamäť s pamäťou?

Po prvé, skontrolujte, ako môže inteligentný alokátor urýchliť prácu s pamäťou.

Budeme písať jednoduché testy pre C ++ a C # (C # je známy pre vynikajúci manažér pamäte, ktorý rozdeľuje objekty pre generácie, používa rôzne bazény pre objekty rôzne veľkosti atď.).

Node triedy (verejnosť: uzol * ďalej;); // ... pre (int i \u003d 0; ja< 10000000; i++) { Node* v = new Node(); }

Uzol triedy (verejný uzol vedľa,) // ... pre (int l \u003d 0; l< 10000000; l++) { var v = new Node(); }

Napriek celku "sféricko-vákuum" príkladu, rozdiel v čase sa ukázal 10-krát (62 ms proti 650 ms). Okrem toho, c # je dokončené, a podľa pravidiel dobrého tónu v C ++, musia byť odstránené určené objekty, ktoré budú ďalej zvyšovať oddelenie (až 2580 ms).

1. Objekty

Zjavným riešením je vyzdvihnúť veľký pamäťový blok z operačného systému a rozdeliť ho na rovnaké bloky veľkosti SEASTOF (uzol), keď prideľujete pamäť, vezmite si blok z bazéna, keď sa uvoľní - vráťte sa do bazéna. Bazén je najjednoduchší na organizovanie s jedným pripojeným zoznamom (zásobníkom).

Vzhľadom k tomu, že stojí za úlohu minimálne rušenia v programe, všetko, čo je možné urobiť, je pridať prímesi blokuLOLOC do triedy uzla:
Uzol triedy: verejný blokALLOC

Po prvé, budeme potrebovať skupinu veľkých blokov (stránok), ktoré odobrajú z OS alebo C-Runtime. Môže byť organizovaný na vrchole funkcií MALLOC a ZADARMO, ale pre väčšiu účinnosť (na preskočenie dodatočnej úrovne abstrakcie), používame VirtualAlloc / VirtualFree. Tieto funkcie prideľujú pamäťové bloky, viacnásobné 4K, rovnako ako rezervovať adresný priestor procesu blokmi, viacerými 64k. Zároveň špecifikujeme možnosti spáchať a rezervné, skočíme ďalšiu úroveň abstrakcie, rezervuje si adresný priestor a zvýraznenie pamäťových stránok s jedným hovorom.

Komentár

inline veľkosť_t zarovnanie (veľkosť_t x, veľkosť_t a) (návrat ((x-1) | (A-1)) + 1;) // # Definujte ALIGN (X, A) ((((x) -1) | (a) -1)) + 1) šablóna Trieda PagePool (Verejnosť: Void * Getpage () (Void * Page \u003d VirtualAllOC (NULL, PagesIze, MEM_COMMIT | MEM_ERSERVE, Page_READWITE); Stránky.push_back (Strana); návratová stránka;) ~ PagePool () (pre (vektor) :: iterator i \u003d pages.begin (); I! \u003d Stránky.end (); ++ i) (virtualFree (* i, 0, MEM_Release);)) Súkromné: Vektor Stránky; );

Potom usporiadajte bazén blokov zadanej veľkosti

Trieda blockpoolu

Šablóny. Trieda BlockPool: PanelPool (Verejnosť: BLOCKPOOL (): HEAD (NULL) (BOOKSIZE \u003d ALIGN (SITEF (T), ZAHRNUTIE); COUNT \u003d PagesIze / Blocksize;) Void * ALLOCBLOCK () (// TODO: Zámok (toto) IF (! HEAD) FormátNewPage (); Vid * TMP \u003d hlava; hlava \u003d * (Void **) Hlava; návrat TMP;) Void FreeBlock (Vid * TMP) (// TODO: Zámok (toto) * (Void **) TMP \u003d hlava; HEAD \u003d TMP;) Súkromné: Void * Head; Size_t Block Blocksize; Size_t COUNTAGE; VOID FORMATIONNESTNOSŤU (VOID * TMP \u003d GETPAGE (); HEAD \u003d TMP; pre (veľkosť_t i \u003d 0; ja< count-1; i++) { void* next = (char*)tmp + BlockSize; *(void**)tmp = next; tmp = next; } *(void**)tmp = NULL; } };

Komentár // TODO: Zámok (toto) Miesta sú označené, ktoré vyžadujú interpotačnú synchronizáciu (napríklad použite Entercriticitsection alebo Boost :: Mutex).

Vysvetlím, prečo, keď "Formátovanie" Stránka nepoužíva abstrakciu freeblock na pridanie bloku do bazéna. Ak bolo napísané niečo podobné

Pre (veľkosť_t i \u003d 0; ja< PageSize; i += BlockSize) FreeBlock((char*)tmp+i);

Táto stránka o princípe FIFO by bola označená "naopak": \\ t

Niekoľko blokov požadovaných z bazéna v rade by malo klesajúcu adresu. A procesor sa neradi vracia, to pretrhne s prefetch ( Up.: Nie je to relevantné moderné spracovatelia). Ak urobíte značku v cykle
Pre (veľkosť_t i \u003d Pagesize- (Blocksize- (Pagesize\u003e Blocksize)); I! \u003d 0; I - \u003d Blocksize) FreeBlock ...
Značkový cyklus by sa dohodol dozadu.

Teraz, keď sa pripravujú, môžete popísať triedu triedy.
Šablóny. Trieda BLOKALLOCALOC (verejnosť: Static Void * Operátor NOVINKA (SIBLY_T S) (ak (s! \u003d SITEOF (t)) (návrat :: prevádzkovateľa nových (-ov);) spätný pool.Allocblock ();) Static Void operation Delete (prázdnota * M, veľkosť_t s) (ak (s! \u003d SITEF (T)) (:: Operátor Odstránenie (M);) INÉ IF (M! \u003d NULL) (bazén .... Static Vid * Operátor NOVINKA (SIBLY_T, VOID * M) (návrat m;) // ... a upozornenie na chýbajúce umiestnenie vymazať ... Static Void operation Delete (Void *, Void *) () Súkromné: Static BlockPool Bazén; ); Šablóny. Blockpool Blokalloc. :: bazén;

Vysvetlím, prečo potrebujete šeky ak (S! \u003d SITEF (t))
Kedy pracujú? Potom, keď je trieda vytvorená / odstránená zo základne T.
Heires budú používať obvyklé nové / odstrániť, ale môžete tiež potrebovať miešať Blokalloc. Preto sme ľahko a bezpečne určovaní, ktoré triedy by mali používať bazény, bez toho, aby sa obávali, že v programe niečo zlomí. Viacnásobné dedičstvo tiež funguje skvele s touto prímenou.

Pripravený. Dedinte uzol z blokuLOALOC a opätovne vykonajte test.
Testovací čas je teraz - 120 ms. 5-krát rýchlejšie. Ale v C # alokátor je stále lepší. Pravdepodobne nie je len pripojený zoznam. (Ak ihneď po nových, okamžite zavolajte, a tak nie ste stráviť veľa pamäte, poznáte údaje v pamäti cache, dostaneme 62 ms. Podivné. Presne ako U.NET CLR, ako keby to okamžite vráti oslobodené lokálne premenné do príslušného bazéna, bez čakania na GC)

2. Kontajner a jeho obsah ropy

Týka sa to často triedy, ktoré uchovávajú veľa rôznych dcérskych spoločností, takže život posledného nie je dlhšia ako životnosť rodiča?

Môže to byť napríklad trieda Xmldocument naplnená uzlom a triedami atribútov, ako aj C-reťazce (char *) z textu vo vnútri uzla. Alebo zoznam súborov a adresárov v správca súborovStiahnuté raz pri opresení adresára a už sa nemení.

Ako bolo uvedené v úvode, odstránenie je drahšie ako nové. Myšlienkou druhej časti článku je prideliť pamäť pre detské predmety vo veľkom bloku spojenej s rodičovským objektom. Pri odstraňovaní rodičovského objektu, dcérske spoločnosti budú ako obvykle spôsobené destruktormi, ale pamäť nebude vrátená - môže sa dostať veľký blok.

Vytvorte triedu PointerBUMPALLOCAtor, ktorá je schopná uhryznúť z veľkého kusu kusov rôznych veľkostí a zvýrazniť nový veľký blok, keď bude starý vyčerpaný.

Trieda PointerBumelocátor

Šablóny. Trieda PointERBUMPLOLOCÁTOR (verejnosť: PointerBumeloCator (): ZADARMO (0) () Void * ALOCBLOCK (SIBLY_T_T BLOCK) (// TODO: Zámok (tento) Block \u003d ALIGN (BLOCK\u003e ZARIADENIE); IF (BLOK\u003e ZADARMO) (Free \u003d ALIGN) (Block, Pagesize); hlava \u003d getpage (zadarmo);) Void * TMP \u003d hlava; hlava \u003d (char *) hlava + blok; voľný - \u003d blok; návrat TMP;) ~ PointerbUumpallocator () (pre (pre (vektor) :: iterator i \u003d pages.begin (); I! \u003d Stránky.end (); ++ i) (virtualFree (* i, 0, MEM_Release);) SÚKROMIE: VOID * GETPAGE (SIBLY_T SIZNUTIA) (Void * Strana \u003d VirtualAlloc (NULL, SIZE, MEM_COMMIT | MEM_RERVE, Page_Readwrite); Stránky.push_back (Strana) ; Návratová stránka;) vektor Stránky; VOID * HEAD; SIMBY_T ZADARMO; ); Typedef PointerBumeloCator<> Defaultovator;

Nakoniec popíšeme prímes na dególií s preťaženým novým a vymazaním, adresovaným na zadanú alloctor:

Šablóny. STRUKTRUMENT DIGHTOPOBJEK (RETURN ALOCACATOR.ALOCKBLOCK (y);) Static Vid * Operátor NOVINKA (RETURN ALOCAKÁTOR-\u003e ALLOCBLOCK (y);) Static Void operation Odstránenie (Void *, SiBly_T) () // * 1 Static Void Operant Odstránenie (prázdnota) *, A *) () Static Void operation Delete (Void *, A &) () Súkromné: Static Vid * Prevádzkovateľ NEW (SIBOTS_T S););

V tomto prípade, okrem pridania nečistoty v triede dieťaťa, bude tiež potrebné napraviť všetky hovory na nové (alebo použiť výrobný vzor). Nový syntax operátora bude nasledovný:

Nové (... Parametre pre operátora ...) FildObject (... Parametre dizajnéra ...)

Pre pohodlie som nastavil dvoch nových operátorov prijímajúcich A & alebo A *.
Ak sa pridáva alokátor k materskej triede ako člen, pohodlnejšie pre prvú možnosť:
Uzol \u003d nový (Alocator) XMLNODE (NODENAME);
Ak sa pridáva alokátor ako predchodca (prímes), je to vhodnejšie pre druhú:
Uzol \u003d nový (tento) XMLNODE (NODENAME);

Špeciálna syntax nie je poskytnutá pre hovoru Vymazanie, kompilátor spôsobí štandardné odstránenie (označené * 1), bez ohľadu na to, čo z nových výrokov bolo použité na vytvorenie objektu. T.j syntax Delete. normálne:
Odstrániť uzol;

Ak existuje výnimka v dizajníri detí (alebo jeho dediču), vymazanie sa nazýva s podpisom zodpovedajúcim podpisu nového operátora, ktorý sa používa pri vytváraní tohto objektu (parameter prvej veľkosti_T bude nahradený prázdnotou *).

Umiestnenie nového prevádzkovateľa v súkromnom sekcii chráni pred novým parametrom.

Dostanem kompletný príklad použitia páru ALLOCACTAGE-CHILDOBJEKCIU:

Príklad

trieda XMLDOCUMENT: Verejnosť DefactyLlocator (verejnosť: ~ XMLDOCUMENT () (pre (vektor) :: iterator i \u003d uzliny.begin (); I! \u003d Uzliny.end (); ++ i) (odstrániť (* i);)) Void addNode (CHAR * Obsah, Char * Meno) (CHAR * C \u003d (CHAR *) ALLOCBLOCK (Strlen (obsah) +1); strcpy (C, obsah); Char * n \u003d (char *) allocblock (strlen (názov) +1); strcpy (N, obsah); uzlí.PUSH_BACK (NOVÝ XMLNODE (C, N));) Trieda XMLNODE: Public ChatObject (Public: XMLNODE (CHAR * _CONTENTE, CHAR * _NAME): Obsah (_Content), názov (_NAME) () Súkromné: CHAR * Content; CHAR * NÁZOV;); Súkromné: Vector uzly; );

Záver. Článok bol napísaný pred 1,5 rokmi pre pieskovisko, ale alas, nepáčilo sa mi moderátor.

    ukladá globálne premenné a konštanty;

    veľkosť sa stanoví pri zostavovaní.

    Stack (Stack)

    ukladá miestne premenné, argumenty s mestskými výpočtovými hodnotami;

    veľkosť je určená pri spustení programu (zvyčajne sa pridelí 4 MB).

    Haldy (haldy)

    dynamicky distribuovaná pamäť;

    OS zvýrazňuje pamäť pre časti (podľa potreby).

Dynamicky distribuovaná pamäť by sa mala používať, ak sme vopred (v čase písania programu) nevieme, koľko pamäte budeme potrebovať (napríklad veľkosť poľa závisí od toho, čo užívateľ vstúpi do programu) a Pri práci s veľkými objemami údajov.

Dynamická pamäť, tiež nazývaná "hromada", sa na žiadosť programu z prostriedkov vyčlenená operačný systém a kontrolované ukazovateľom. Nie je to automaticky a mali by byť jasne uvoľnené. Na rozdiel od statickej a automatickej pamäte je dynamická pamäť prakticky neobmedzená (obmedzená iba veľkosťou náhodný vstup do pamäťe) a môže sa zmeniť počas programu

Práca s dynamickou pamäťou

Ak chcete pracovať s dynamickou pamäťou v jazyku, používajú sa nasledujúce funkcie: mALLOC, CALLOC, ZADARMO, REALLOC. Podrobnejšie ich zvážiť.

    Výber (zachytávanie pamäte): Void * MALLOC (veľkosť SIBER_T_T);

Ako vstupný parameter, funkcia má veľkosť pamäte, ktorú chcete zvýrazniť. Vrátená hodnota je ukazovateľ na stránku pamäte pridelenej v hromade. Ak OS nebol schopný prideliť pamäť (napríklad pamäť nestačí), potom sa Malloc vráti 0.

    Po skončení práce s pridelenou dynamickou pamäťou, ktorú potrebujete zadarmo. Na tento účel sa použije voľná funkcia, ktorá vráti pamäť na riadené OS: Void Free (Void * PTR);

Ak je pamäť uvoľnená pred koncom programu, je automaticky vyňať po ukončení programu. Explicitné vydanie nepotrebnej pamäte je však znakom dobrého programovacieho štýlu.

Príklad: // Pridelenie pamäte pod 1000 prvkov typu Int

int * p \u003d (int *) malloc (1000 * SITEOF (INT));

ak (p \u003d\u003d null) cout<< "\n память не выделена";

zadarmo (p); // vrátenie pamäte do partiu

2. Pridelenie (zachytávanie pamäte): Void * CALLOC (SIBLY_T NMEMB, SIZNUČNÁ veľkosť);

Funkcia funguje podobne ako MALLOC, ale je charakterizovaný syntaxom (namiesto veľkosti pridelenej pamäte, musíte zadať počet prvkov a veľkosti jedného prvku) a skutočnosť, že vybratá pamäť sa resetuje. Napríklad po vykonaní int * p \u003d (INT *) CALLOC (1000, SITEF (INT)) P, P ukážte na začiatok art-typu Array 1000 prvkov inicializovaných pomocou nuly.

3. Zmena veľkosti pamäte: VOID * REALLOC (VOID * PTR, SIZNUTIE SIZNUTIE);

Funkcia mení veľkosť pridelenej pamäte (ktorá označuje ptr, Prijaté z hovoru mALLOC, CALLOC ALEBO REALLOC). Ak veľkosť uvedená v parametri veľkosť viac ako ten, ktorý bol zdôraznený pod ukazovateľom ptr, Skontroluje sa, či je možnosť zvýrazniť chýbajúce bunky pamäte v rade s už venovaným. Ak nie je dostatok miesta, potom sa rozlišuje nová časť pamäte veľkosť a údaje o indikátoroch pTR. Kopírovať na začiatok novej stránky.

V procese vykonania programu je dynamická pamäťová stránka k dispozícii všade, kde je k dispozícii ukazovateľ ukazovateľa. Nasledujúce tri možnosti sú teda možné s dynamickou pamäťou pridelenou v určitej jednotke (napríklad v intenzívnej funkcii).

    Ukazovateľ (na lokalitu dynamickej pamäte) je definovaný ako lokálny objekt automatického pamäte. V tomto prípade nie je zvolená pamäť k dispozícii, keď blok blokuje blok bloku, a je potrebné ho pred ukončením bloku uvoľniť.

(INT * P \u003d (INT *) CALLOC (N, SITEOF (INT))

zadarmo (p); // vydanie DIN. Pamäť

    Ukazovateľ je definovaný ako lokálna statická pamäť. Dynamická pamäť pridelená raz v bloku je k dispozícii prostredníctvom ukazovateľa pri každom zadržiavaní v bloku. Pamäť je potrebné uvoľniť len na konci svojho používania.

(Statický int * p \u003d (int *) callOc (N, SITEOF (INT));

p \u003d (INT *) CALLOC (N, SITEOF (INT));

f (50); // dean alokácia. Pamäť s následným uvoľnením

f1 (100); // dean alokácia. Pamäť (prvé odvolanie)

f1 (100); // pracovať s dekanom. Pamäť

f1 (0); // vydanie DIN. Pamäť

    Ukazovateľ je globálnym objektom s ohľadom na blok. Dynamická pamäť je k dispozícii vo všetkých blokoch, kde je ukazovateľ "viditeľný". Pamäť je potrebné uvoľniť len na konci svojho používania.

int * pg; // Pracovný ukazovateľ pre Dean. Pamäť (globálna premenná)

void init (int veľkosť)

pre (i \u003d 0; ja< size; i++) //цикл ввода чисел

(Printf ("x [% d] \u003d", i);

sCANF ("% D", & PG [I]);

inT suma (int veľkosť)

pre (i \u003d 0; ja< size; i++) //цикл суммирования

// Pridelenie pamäte

pg \u003d (INT *) CALLOC (N, SITEOF (INT));

// pracovať s dekanom PAM

printf (ns \u003d% d n ", súčet (n));

zADARMO (PG); pg \u003d null; // Uvoľnenie pamäte

Práca s dynamickou pamäťou v C ++

V C ++ je mechanizmus na pridelenie a uvoľnenie pamäte - to sú funkcie. nový a vymazať.Príklad použitia nový: Int * p \u003d nový int; // Výber pamäte pod 1000 el-poradie, tj. Pri používaní funkcie nový Nie je potrebné dať ukazovateľ a nemusíte používať veľkosť (). Uvoľnenie pridelené nový Pamäť sa vykonáva podľa nasledujúceho hovoru: Odstránenie P; Ak chcete vybrať pamäť pre jeden prvok, môžete použiť INT * Q \u003d NEW INT; alebo int * q \u003d nový int (10); // Vybrané Int inicializované hodnotou 10 v tomto prípade, odstránenie bude vyzerať takto: odstrániť q;

čas programy. Pod miestnymi premennými, program má pamäť z miesta zásobníka. Miestne premenné však vyžadujú predbežné určenie množstva pamäte pridelenej pre každú situáciu. Hoci C ++ účinne implementuje takéto premenné, vyžadujú, aby programátor poznal vopred, aké množstvo pamäte je potrebná pre každú situáciu.

Druhý spôsob, ako C ++ môže ukladať informácie, je použitie systému dynamickej distribúcie. V tejto metóde je pamäť distribuovaná pre informácie z voľnej oblasti pamäte podľa potreby. Oblasť voľnej pamäte je medzi programovým kódom s konštantnou pamäťou a zásobníkom (obr. 24.1). Dynamické ubytovanie je vhodné, keď nie je známe, koľko dátových položiek bude spracovaných.


Obr. 24.1.

Keďže program používa oblasť zásobníka, ktorá sa zvyšuje, to znamená, že samotný program určuje hlasitosť pamäte zásobníka. Napríklad veľký číselný program rekurzívne funkcie bude trvať viac pamäte zásobníka ako program, ktorý nemá rekurzívne funkcieVzhľadom k tomu, lokálne premenné a spätné adresy sú uložené v zásobníkoch. Pamäť pre samotný program a globálne premenné vyniknúť na všetko čas Programy sú konštantné pre konkrétne prostredie.

Pamäť pridelená v procese vykonávania programu sa nazýva dynamická. Po výbere dynamický Je zachránený na jeho explicitné vydanie, ktoré možno vykonávať len so špeciálnou funkciou operácie alebo knižnice.

Ak dynamická pamäť nie je uvoľnená pred koncom programu, je automaticky vyňať po ukončení programu. Explicitné vydanie nepotrebnej pamäte je však znakom dobrého programovacieho štýlu.

V procese vykonania programu je dynamická pamäťová stránka k dispozícii všade, kde je k dispozícii ukazovateľ ukazovateľa. Možné sú teda možné. tri pracovné možnosti s dynamickou pamäťouvylučovaný v určitej jednotke (napríklad v telesnom tele).

  • Ukazovateľ (na lokalitu dynamickej pamäte) je definovaný ako lokálny objekt automatického pamäte. V tomto prípade nie je zvolená pamäť k dispozícii, keď blok blokuje blok bloku, a je potrebné ho pred ukončením bloku uvoľniť.
  • Ukazovateľ je definovaný ako lokálna statická pamäť. Dynamická pamäť pridelená raz v bloku je k dispozícii prostredníctvom ukazovateľa pri každom zadržiavaní v bloku. Pamäť je potrebné uvoľniť len na konci svojho používania.
  • Ukazovateľ je globálnym objektom s ohľadom na blok. Dynamická pamäť je k dispozícii vo všetkých blokoch, kde je ukazovateľ "viditeľný". Pamäť je potrebné uvoľniť len na konci svojho používania.

Všetky premenné deklarované v programe sú umiestnené v jednej oblasti nepretržitej pamäti segment údajov. Takéto premenné nemenia svoju veľkosť počas realizácie programu a sú nazývaní statický. Veľkosť segmentu údajov nemusí stačiť na umiestnenie veľkých množstiev informácií. Výstupom z tejto situácie je použitie dynamickej pamäte. Dynamická pamäť - Toto je pamäť pridelená programom pre svoju prácu menej dátového segmentu, stoh, v ktorom sú umiestnené lokálne premenné podprogramov a samotného programu.

Ukazovatele používajú ukazovatele na prácu s dynamickou pamäťou. S ich pomocou, prístup k dynamickej pamäti, ktoré sa nazývajú dynamické premenné. Pre skladovanie dynamické premenné Oblasť špeciálnej pamäte je zvýraznená, nazvaná "chyby".

Dynamické premenné Vytvorené so špeciálnymi funkciami a operáciami. Existujú buď až do konca programu, alebo kým sa pamäť pridelená na ne uvoľní pomocou špeciálnych funkcií alebo operácií. To je život dynamické premenné - Z miesta vytvorenia až po koniec programu alebo explicitne uvoľnite pamäť.

C ++ používa dva spôsoby práce s dynamickou pamäťou:

  1. používanie operácií nové a odstrániť;
  2. použitie rodiny funkcií Malcos (Calloc) (zdedená z C).

Práca s dynamickou pamäťou pomocou nových a odstránení operácií

V programovom jazyku C ++ pre dynamická distribúcia pamäte Existujú operácie nové a odstrániť. Tieto operácie sa používajú na zvýraznenie a uvoľnenie pamäťových blokov. Oblasť pamäte, v ktorej sú tieto bloky umiestnené, nazývané bezplatná pamäť.

Nová operácia umožňuje zvýrazniť a vytvoriť prístupný bezplatný pozemok v hlavnej pamäti, ktorých rozmery zodpovedajú typu dát definovaných typom.

Syntax:

nový názovYPE;

nový názovmepe [inicializátor];

Zvýraznená oblasť je zadaná hodnotou určenou inicializátornie je povinným prvkom. V prípade úspešného vykonania sa nový vráti adresu začiatku zvolenej oblasti pamäte. Ak nie je možné zvoliť časť požadovaných rozmerov (bez pamäte), nová operácia vráti hodnotu nulovej adresy (null).

Operácia aplikácie Syntax:

Ukazovateľ \u003d nový názovYPE [inicializátor];

Nová prevádzka plaváka zvýrazňuje oblasť pamäte 4 bajtov. Nová operácia INT (15) zvýrazňuje oblasť pamäte 4 bajtov a inicializuje túto stránku s celú hodnotu 15. Syntax sieťového používania a odstráni sa používa používanie ukazovateľov. Predtým musí byť oznámená každý ukazovateľ:

typ * Indikátor mena;

Napríklad:

float * pi; // Oznámenie PI PI \u003d nová plaváková premenná; // Výber pamäte pre variabilné PI * PI \u003d 2,25; // Priradenie hodnoty

Ako napríklad typ, ako napríklad štandardné typy. int, dlhý, plavák, dvojitý, char.

Nový operátor sa najčastejšie používa na hosťovanie typov špecifických pre typové špecifické v pamäti údajov, napríklad štruktúr:

sprievodný uzol (znak * názov; hodnota Int; uzol * ďalej); Uzol * PNODE; // oznámili PNODE \u003d Nový ukazovateľ uzla; // PNODE-\u003e NAME \u003d "ATA"; // prideľuje hodnoty pNode-\u003e \u003d 1 hodnotám; Póda-\u003e nasledujúci \u003d null;

Ako typ názvu v novej prevádzke je možné použiť pole:

nOVÉ TIMYSIVA

Keď prideľujete dynamickú pamäť pre pole, musia byť jeho veľkosti plne definované. Napríklad:

ptr \u003d nový int; // 10 prvkov typu Int alebo 40 bajtov ptr \u003d nový int; // nesprávne, pretože Nie je definovaná veľkosť

Takáto operácia umožňuje zvýrazniť oblasť v dynamickej pamäti, aby sa umiestnila pole zodpovedajúceho typu, ale neumožňuje, aby sa inicializoval. V dôsledku realizácie sa nová operácia vráti ukazovateľ, ktorej hodnota je adresa prvého prvku poľa. Napríklad:

int * n \u003d nový int;

Nová operácia vykonáva dynamický typ pamäte, ktorý je dostatočný na umiestnenie hodnoty int a zaznamenáva adresu začiatku tejto oblasti do premennej N. Pamäť pod variabilnou n (veľkosť dostatočná na umiestnenie ukazovateľa) je zvýraznená na fáze kompilácie.

Veľmi často existujú úlohy spracovania súborov údajov, ktorých rozmer je vopred neznámy. V tomto prípade je možné použiť jeden z dvoch prístupov:

  • výber pamäte pre statické pole obsahujúce maximálny možný počet prvkov, ale v tomto prípade nie je pamäť spotrebovaná racionálnym;
  • dynamické pridelenie pamäte na ukladanie súboru údajov.

Ak chcete používať dynamické funkcie prideľovania pamäte, je potrebné opísať ukazovateľ, ktorý je počiatočnou adresou ukladania prvok poľa.

int * p; // ukazovateľ na zadanie INT

Počiatočná adresa statického poľa je určená kompilátorom v čase jej oznámenia a nie je možné zmeniť.

Pre dynamické pole je počiatočná adresa priradená deklarovaným ukazovateľom na pole počas realizácie programu.

Štandardná dynamická alokácia pamäte

Funkcie dynamickej pamäte sa nachádza v RAM, nepretržitá časť požadovanej dĺžky a vráti pôvodnú adresu tejto oblasti.

Dynamické funkcie distribúcie pamäte:

vOID * MALLOC (veľkosť masivovateľná);
void * Calloc (number, veľkosť elementavabajty);

Ak chcete použiť funkcie dynamickej distribúcie pamäte, musíte knižnicu pripojiť. :

#Include.

Vzhľadom k tomu, obe prezentované funkcie ako vrátená hodnota majú ukazovateľ na prázdny typ prázdniny, vyžaduje sa explicitné pripisovanie typu návratnosti hodnoty.

Ak chcete určiť veľkosť poľa v bajtoch, ktorá sa používa ako argument funkcie MALLOC (), je potrebné počítať prvky na násobenie jedným prvkom. Vzhľadom k tomu, prvky poľa môžu byť tieto typy dátových a kompozitných typov (napríklad konštrukcie), aby presne určili veľkosť prvku všeobecne odporúčané použitie funkcie

int veľkosť (typ);


ktorý definuje množstvo bajtu obsadeného prvkom špecifikovaného typu.

Pamäť, dynamicky zvýraznená pomocou CALLOC (), MALLOC () funkcie, môže byť uvoľnená pomocou funkcie

voľný (ukazovateľ);

"Pravidlo spoločného tónu" v programovaní je uvoľnenie dynamicky pridelenej pamäte v neprítomnosti jej ďalšieho používania. Avšak, ak dynamicky vyhradená pamäť nie je oslobodená, bude vypustený po ukončení vykonávania programu.

Dynamická alokácia pamäte pre jednorozmerné polia

Forma cirkulácie na prvky poľa pomocou ukazovateľov je nasledujúci formulár:

int a, * p; // Opíšte statické pole a ukazovateľ
int b;
p \u003d a; // priradiť počiatočnú adresu poľa
... // zadanie prvkov poľa
b \u003d * p; // b \u003d A;
b \u003d * (p + i) // b \u003d a [i];

Príklad na C: organizovanie dynamického jednorozmerného poľa a vstupu jej prvkov.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


#Include.
#Include.
#Include.
int hlavné ()
{
int * a; // ukazovateľ na pole
int i, n;
("CHCP 1251");
("CLS");
Printf ( "Zadajte veľkosť poľa:");
SCANF ("% D", & N);
// Pridelenie pamäte
A \u003d (INT *) MALLOC (N * SITEF (INT));
// zadanie prvkov poľa
pre (i \u003d 0; ja {
Printf ("A [% d] \u003d", i);
SCANF ("% D", & A [I]);
}
// uzavretie prvkov poľa
pre (i \u003d 0; ja Printf ("% D", A [I]);
ZADARMO (A);
getchar (); getchar ();
návrat 0;
}


Výsledok programu:

Dynamické pridelenie pamäte pre dvojrozmerné polia

Nechajte sa konať v dynamickej pamäti matricu obsahujúcu n reťazce a m stĺpce. Dvojrozmerná matrica bude umiestnená v RAM v tvare pásky pozostávajúcej z prvkov reťazcov. V tomto prípade sa index akéhokoľvek prvku dvojrozmernej matrice môže získať vzorcom

index \u003d i * m + j;

kde som aktuálne číslo riadku; J je číslo aktuálneho stĺpca.

Zvážte 3x4 matricu (pozri obr.)

Index vyhradeného prvku bude určený ako

index \u003d 1 * 4 + 2 \u003d 6

Množstvo pamäte potrebnej na umiestnenie dvojrozmerného poľa sa stanoví ako

n · m · (veľkosť elementu)

Avšak, pretože s týmto vyhlásením, kompilátor jasne jasne neuvádza počet prvkov v riadku a stĺpci dvojdimenzionálneho poľa, tradičné odvolanie na položku zadaním indexu reťazca a index stĺpca je nesprávny:

a [i] [j] - nesprávne.

Správna výzva na prvku pomocou ukazovateľa bude vyzerať

* (P + i * m + j),
Kde

  • p je ukazovateľ poľa
  • m - počet stĺpcov, \\ t
  • i - Riadkový index,
  • j je index stĺpca.

Príklad na Si

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

#Define _crt_secure_no_warnings.
#Include.
#Include.
#Include.
int hlavné ()
{
int * a; // ukazovateľ na pole
int i, j, n, m;
("CHCP 1251");
("CLS");
Printf ( "Zadajte počet riadkov:");
SCANF ("% D", & N);
Printf ();
SCANF ("% D", & m);
// Pridelenie pamäte
A \u003d (INT *) MALLOC (N * M * SIZEOF (INT));
// zadanie prvkov poľa
pre (i \u003d 0; ja // riadkový cyklus
{
pre (j \u003d 0; j // cyklus na stĺpci
{
SCANF ("% D", (A + I * M + J));
}
}
// uzavretie prvkov poľa
pre (i \u003d 0; ja // riadkový cyklus
{
pre (j \u003d 0; j // cyklus na stĺpci
{
Printf ("% 5D", * (A + I * M + J));
}
Printf ("n");
}
ZADARMO (A);
getchar (); getchar ();
návrat 0;
}

Výsledok realizácie

Ďalšia metóda dynamickej pamäte je tiež možná aj pre dvojrozmerné pole - pomocou radu ukazovateľov. Na to potrebujete:

  • vyberte jednotku RAM pod radou ukazovateľov;
  • vyberte bloky RAM pre jednorozmerné polia, ktoré sú radmi požadovanej matrice;
  • záznamové adresy riadkov do radu ukazovateľov.

Graficky takýto spôsob prideľovania pamäte môže byť zastúpený nasledovne.


S touto metódou je veľkosť kompilátora jasne označená počtom riadkov a počtom stĺpcov v poli.
Príklad na Si

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#Define _crt_secure_no_warnings.
#Include.
#Include.
#Include.
int hlavné ()
{
int ** a; // ukazovateľ na ukazovateľ na riadok prvkov
int i, j, n, m;
("CHCP 1251");
("CLS");
Printf ( "Zadajte počet riadkov:");
SCANF ("% D", & N);
Printf ( "Zadajte počet stĺpcov:");
SCANF ("% D", & m);
// Výber pamäte pre riadky
// zadanie prvkov poľa
pre (i \u003d 0; ja // riadkový cyklus
{
// Výber pamäte pre úložné struny
a [i] \u003d (INT *) MALLOC (M * SITEF (INT));
pre (j \u003d 0; j // cyklus na stĺpci
{
Printf ("A [% d] [% d] \u003d", I, J);
SCANF ("% D", & A [I] [j]);
}
}
// uzavretie prvkov poľa
pre (i \u003d 0; ja< n; i++) // riadkový cyklus
{
pre (j \u003d 0; j< m; j++) // cyklus na stĺpci
{
Printf ("% 5D", A [I] [J]); // 5 známym pod prvkom poľa
}
Printf ("n");
}
// Čistenie pamäte
pre (i \u003d 0; ja< n; i++) // riadkový cyklus
ZADARMO (A [I]); // oslobodenie pamäte pre riadok
ZADARMO (A);
getchar (); getchar ();
návrat 0;
}

Výsledok programu je podobný predchádzajúcemu prípadu.

Používanie dynamického prideľovania pamäte pod ukazovateľmi riadkov môžete umiestniť voľné polia. Dvojrozmerné pole (matrix) je zadarmo, ktorých veľkosť reťazca môže byť iná. Výhodou používania voľného poľa je, že nie je potrebné odstrániť pamäť počítača s rezervou na umiestnenie reťazca maximálnej možnej dĺžky. V skutočnosti je voľné pole jednorozmerné pole ukazovateľov na jednozídlové dátové polia.

Ak chcete ubytovať maticu v pamäti RAM s riadkami rôznych dĺžok, musíte zadať ďalšie pole M, v ktorom sa skladuje veľkosť reťazcov.

Príklad na Si

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#Define _crt_secure_no_warnings.
#Include.
#Include.
int hlavné ()
{
int ** a;
int i, j, n, * m;
("CHCP 1251");
("CLS");
Printf ( "Zadajte počet riadkov:");
SCANF ("% D", & N);
A \u003d (INT **) MALLOC (N * SITEF (INT *));
m \u003d (INT *) MALLOC (N * SITEOF (INT)); // Array počet prvkov v radových riadkoch A
// zadanie prvkov poľa
pre (i \u003d 0; ja {
Printf ( "Zadajte počet stĺpcov% D:", i);
SCANF ("% D", & M [I]);
a [i] \u003d (INT *) MALLOC (M [I] * SITEOF (INT));
pre (j \u003d 0; j Printf ("A [% d] [% d] \u003d", I, J);
SCANF ("% D", & A [I] [j]);
}
}
// uzavretie prvkov poľa
pre (i \u003d 0; ja {
pre (j \u003d 0; j {
Printf ("% 3D", a [i] [j]);
}
Printf ("n");
}
// Uvoľnenie pamäte
pre (i \u003d 0; ja< n; i++)
{
ZADARMO (A [I]);
}
ZADARMO (A);
Voľné (m);
getchar (); getchar ();
návrat 0;
}


Výsledok realizácie

Redistribúcia pamäte

Ak veľkosť pamäte pamäte nemôže byť zadaná vopred, napríklad pri zadaní postupnosti hodnôt na konkrétny príkaz, potom zvýšiť veľkosť poľa, keď zadáte nasledujúcu hodnotu, musíte vykonať Nasledujúce kroky:

  • Vyberte rozmerovú jednotku n + 1 (1 viac ako aktuálna veľkosť poľa)
  • Skopírujte všetky hodnoty uložené v poli v novo vyhradenej oblasti pamäte.
  • Voľná \u200b\u200bpamäť pridelená skôr pre skladovanie poľa
  • Posunutím ukazovateľa spustite pole na začiatku novozvolenej oblasti pamäte.
  • Pridajte hodnotu poslednú hodnotu poľa

Všetky vyššie uvedené kroky (okrem poslednej) vykonáva funkciu

vOID * REALLOC (VOID * PTR, SIVERE_T SIZE);

  • pTR je ukazovateľ k bloku predtým pridelenej pamäte s Malloc (), Calloc () funkciami alebo prejsť na nové miesto. Ak je tento parameter NULL, nová jednotka je pridelená a funkcia naň vracia ukazovateľ.
  • veľkosť je nová veľkosť, v bajtoch prideleného pamäťového bloku. Ak je veľkosť \u003d 0, predtým vyhradená pamäť a funkcia vráti nulový ukazovateľ, PTR je nainštalovaný v null.

Veľkosť pamäťového bloku, na ktorú sa parameter PTR oznamuje zmeny veľkostí bajtov. Pamäťový blok sa môže znížiť alebo zvýšiť. Obsah pamäte bloku sa zachová, aj keď má nová jednotka menšiu veľkosť ako starý. Ale tieto údaje, ktoré presahujú nový blok, sa vyhodia. Ak je nový pamäťový blok väčší ako starý, obsah novo pridelenej pamäte bude neistý.
iF (I\u003e 2) I - \u003d 2;
Printf ("n");
A \u003d (Int *) Realloc (A, I * SITEOF (INT)); // Zníženie veľkosti poľa pre 2
pre (int j \u003d 0; j< i; j++)
Printf ("% D", A [J]);
getchar (); getchar ();
návrat 0;
}

Program môže ukladať informácie do hlavnej pamäte počítača v dvoch hlavných smeroch. Prvý z nich využíva globálne a miestne premenné, vrátane polí, štruktúr a tried. V prípade globálnych a statických miestnych premenných je umiestnenie skladovania stanovené pre celý čas realizácie programu. V prípade lokálnych premenných je pamäť zvýraznená v zásobníku. Aj keď v Borland C ++, práca s týmito premennými je implementovaná veľmi efektívne, ich použitie vyžaduje, aby programátor mohol vedieť vopred veľkosť pamäte, ktorá bude potrebná počas realizácie programu.

Druhým spôsobom ukladania informácií je použitie systému dynamickej pamäte Borland C ++ pamäte. V tejto metóde je pamäť na ukladanie informácií pridelená z voľnej oblasti pamäte podľa potreby a vráti sa späť, t.j. Je to uvoľnené, keď je potreba zmizla. Oblasť voľného pamäte leží medzi oblasťou pamäte, kde sa nachádza program a zásobník. Táto oblasť sa nazýva parta (halda) a používa sa na požiadavky na pridelenie dynamickej pamäte.

Výhodou používania dynamickej pamäte je, že rovnaká pamäť môže byť použitá na uloženie rôznych informácií v procese realizácie programu. Keďže pamäť je pridelená na konkrétny účel a je uvoľnený, keď jeho použitie bolo dokončené, môžete použiť rovnakú pamäť na inom čase na iné účely v inej časti programu. Ďalšou výhodou alokácie dynamickej pamäte je schopnosť vytvárať súvisiace zoznamy, binárne stromy a iné dynamické dátové štruktúry.

Jadrom alokácie dynamickej pamäte pamäte jazyka s funkciami Func () a bezplatné (), ktoré sú súčasťou štandardnej knižnice. Vždy, keď je funkcia MALLOC (), pamäť je prijatá na prideľovanie pamäte, odlišuje sa časť voľnej pamäte. Kedykoľvek sa táto pamäť uvoľní pomocou funkcie Free (), táto pamäť vráti systém.

Jazyk C ++ definuje dve dynamické operátory prideľovania pamäte - nové a odstrániť.

Štandard ANSI C definuje iba štyri funkcie dynamickej pamäte: CALLOC (), MALLOC (), ZADARMO () a REALLOC (). Borland C ++ však obsahuje niekoľko ďalších funkcií dynamickej pamäte. Pri kompilácii kódu pre moderný 32-bitový pamäťový model je pamäť rovinatý a zvyčajne používa len štyri štandardné funkcie prideľovania pamäte.

Štandard ANSI C definuje, že informácie o hlavičke potrebné pre dynamické pridelenie pamäte sú obsiahnuté v súbore STDLIB.H. Borland C ++ vám však umožňuje používať súbory STDLIB.H alebo ALLOC.H. Tu používame súbor hlavičky STDLIB.H, pretože poskytuje prenosnosť. Niektoré ďalšie funkcie alokácie dynamickej pamäte vyžadujú súbory hlavičky spoločnosti ALLOC.H, Malloc.h alebo DOS.H. Je potrebné venovať osobitnú pozornosť, ku ktorej je potrebný súbor hlavičky na používanie každej funkcie.



Páči sa vám článok? Zdieľaj to