Névjegyzék

C A karakterlánc memóriájának kiválasztása. Dinamikus memóriaelosztás. Hogyan kell kiosztani az új üzemeltető memóriáját azzal a kritikus helyzet leküzdésével, amelyben a memória nem áll fenn? Kivételes helyzet BAD_ALLOC. Példa

A dinamikus memóriával való együttműködés gyakran sok algoritmusban szűk keresztmetszet, ha nem alkalmaz speciális trükköket.

A cikkben néhány ilyen technikát veszek figyelembe. A cikkben szereplő példák eltérnek (például), mivel az új és a törlési üzemeltetők túlterhelését használják, és ennek köszönhetően a szintaxisszerkezetek minimalizálódnak, és a program remake egyszerű. Szintén leírta a víz alatti kövek (természetesen a guru, aki elolvassa a standardot a kéreghéjból, nem lesz meglepve).

0. Szükségünk van manuális memóriára a memóriával?

Először is, ellenőrizze, hogy egy intelligens elosztó felgyorsíthatja a munkát a memóriával.

Egyszerű teszteket fogunk írni a C ++ és a C # (C #), amely kiváló memóriakezelőről ismert, amely az objektumokat generációkra osztja, különböző célokat használ az objektumok számára különböző méretek stb.).

Osztálycsomópont (nyilvános: csomópont * Következő;); // ... az (int i \u003d 0; i)< 10000000; i++) { Node* v = new Node(); }

Osztály csomópont (nyilvános csomópont Következő;) // ... for (int l \u003d 0; l< 10000000; l++) { var v = new Node(); }

A példa teljes "gömb alakú vákuum" ellenére a különbség az idő 10-szer kiderült (62 ms ellen 650 ms). Ezenkívül a C # befejeződött, és a C ++ jó hangjelzési szabályai szerint a dedikált objektumokat el kell távolítani, ami tovább növeli az elválasztást (legfeljebb 2580 MS).

1. Medence objektumok

A nyilvánvaló megoldás az, hogy felvegye egy nagy memóriablokkot az OS-ből, és megosztja azt egyenlő méretű méretű méretű (csomópont), amikor elosztja a memóriát, vegyen egy blokkot a medencéből, amikor megjelenik - visszatérjen a medencébe. A medence a legkönnyebben szervezhető egy csatlakoztatott listával (verem).

Mivel érdemes a minimális beavatkozás feladata a programban, mindent megtehetünk, hogy hozzáadjon egy blokkloc összekeverését a csomópontosztályhoz:
Osztálycsomópont: nyilvános blokklocc

Először is, szükségünk lesz egy nagy blokkra (oldalak), amely elviszi az OS-t vagy a C-Runtime-t. A malloc és a szabad funkciók tetején szervezhető, de a nagyobb hatékonyság érdekében (az absztrakció extra szintjének kihagyásához) virtualalloc / virtualfree használata. Ezek a funkciók a memóriablokkokat, a többszörös 4k-ot, valamint a folyamat címterét blokkolással, többszörös 64k-ig tartják. Ugyanakkor meghatározza a kötelezettségvállalás és a tartalék opciókat, ugorjunk egy másik szintet az absztrakció, a címterület fenntartása, és kiemelve a memória oldalakat egy hívással.

Oldalpool

inline sele_t igazítás (size_t x, size_t a) (Visszatérés (X-1) | (A-1)) + 1;) // # definiálja az igazságot (x, a) | (((x) -1) | (A) -1)) + 1) sablon Class PagePool (nyilvános: Void * Getpage () (Void * Page \u003d VirtualAllLOC (NULL, PAGESSIZE, MEM_COMMIT | MEM_RERSERVE, PAGE_READWRITE); oldalak.push_back (oldal); visszatérő oldal;) ~ PagePool () :: iterator i \u003d oldalak.begin (); I! \u003d Oldalak.end (); ++ i) (virtualfree (* i, 0, mem_release);)))): vektor Oldalak; );

Ezután szervezze meg a megadott méretű blokkok medencéjét

Blokkoló osztály

sablon. Class Blockpool: PagePool (Nyilvános: BLOCKPOOL (): fej (null) (blokkolás \u003d igazítás (sizeof (t), igazítás); szám \u003d Pageize / Blocksize;) Void * Allocblock () FormátumNewage (), üresség * tmp \u003d fej; fej \u003d * (üresség **) fej; visszatérés tmp;) Void Freeblock (Void * TMP) (// TODO: LOCK (VOID **) TMP \u003d fej; Fej \u003d tmp;) Privát: Void * Head; Méret_t Blocksize; Méret_t szám; Void FormatNewage () (Void * TMP \u003d GetPage (); fej \u003d tmp;< count-1; i++) { void* next = (char*)tmp + BlockSize; *(void**)tmp = next; tmp = next; } *(void**)tmp = NULL; } };

Megjegyzés // todo: zár (ez) Helyek vannak jelölve, amelyek előírják, interpotional szinkronizálás (például használatra EnterCriticalSection vagy kiemelés :: Mutex).

Megmagyarázom, hogy miért "formázás" Az oldal nem használja a szabadblokk absztrakciót, hogy hozzáadjon egy blokkot a medencébe. Ha valami ilyesmit hasonlítottak meg

Mert (size_t i \u003d 0; i< PageSize; i += BlockSize) FreeBlock((char*)tmp+i);

Ez az oldal a FIFO elve alapján "ellentétes" lennének:

A medencéből egy sorban kért több blokknak csökkenő címe lenne. És a processzor nem szeret vissza menni, megszakítja az előzetesen ( UPD.: Nem releváns a modern processzorok számára). Ha a ciklusban jelöli
Mert (size_t i \u003d pageize- (Blocksize- (Pageized% Blocksize)); I! \u003d 0; i - \u003d blokkolás) Freeblock ...
A jelölőciklus visszafelé haladna.

Most, hogy az előkészületek készülnek, leírhatja a fokozatosztályt.
Sablon. Osztályblokkot (nyilvános: statikus üresség * operátor új (size_t s) (ha (S! \u003d Sizeof (t)) (Visszatérés :: Operátor új (ek);) visszatérő pool.Allocblock ();) statikus üres kezelői törlés * M, size_t s) (ha (S! \u003d Sizeof (t)) (:: Operátor törlése (m);) más, ha (m! \u003d Null) (medence .... statikus üresség * operátor új (size_t, üresség) * M) (M;) // ... és a hiányzó elhelyezés törlése ... statikus üres üzemeltető törlése (Void *, Void *) () Privát: statikus Blockpool Medence; ); Sablon. Csökönyös Blockalloc. :: Medence;

Megmagyarázom, hogy miért kell ellenőrizni ha (S! \u003d sizeof (t)))
Mikor működnek? Akkor az osztály létrehozása / törlése az alapból T.
Az örökösök a szokásos új / törlést fogják használni, de a Blockalloc-t is el kell keverniük. Így könnyen és biztonságosan meg kell határozni, hogy melyik osztályoknak kell használniuk a medencéket, anélkül, hogy félnünk valamit a programban. A többszörös öröklés is nagyszerűen működik ezzel a keverékkel.

Kész. A blokkloc csomópontjának örökölése és a teszt újragondolása.
A tesztidő most - 120 ms. 5-szer gyorsabb. De a C # Allocator még mindig jobb. Valószínűleg nincs csak egy összekapcsolt lista. (Ha az új, az új, az új, hívás törlése, és így nem költünk sok memóriát, ismerjük az adatokat a gyorsítótárban, 62 ms-ot kapunk. Furcsa. Pontosan olyan, mint egy U.NET CLR, mintha azonnal visszaadja a felszabadult helyi változókat az érintett medencére, a GC várakozás nélkül)

2. Konténer és kőolajtartalma

Gyakran találkozik olyan osztályok között, amelyek sok különböző leányvállalatot tárolnak, úgy, hogy az utolsó élettartama nem hosszabb, mint a szülő élettartama?

Például lehet egy xmldocument osztály, amely tele van csomópontokkal és attribútumosztályokkal, valamint a csomóponton belüli szövegből származó C-Strings (Char *). Vagy a fájlok és könyvtárak listája fájl kezelőEgyszer letöltötte a könyvtárat, és már nem változik.

Amint azt a bevezetés mutatja, a törlés drágább, mint az új. A cikk második részének ötlete, hogy a szülőkobjektumhoz tartozó nagy blokkban lévő gyermekobjektumok memóriáját hozza ki. A szülői objektum eltávolításakor a leányvállalatok a szokásos módon a destruktorok által okozottak, de a memória nem fog visszatérni - szabadon kaphat nagy blokkot.

Hozzon létre egy Pointerbumpallocator osztályt, amely képes megharapni egy nagy darab különböző méretű darabokat, és kiemeli az új nagy blokkot, amikor a régi kimerül.

Pointerbumpallocator osztály

sablon. CLASS POINTERBUMPLOLLOCATOR (nyilvános: Pointerbumpallocator (): Ingyenes (0) () Void * ALLOCBLOCK (MÉRET_T BLOCK) (// TODO: LOCK (ez) blokk \u003d igazítás (blokk, igazítás); ha (blokk\u003e szabad) (szabad \u003d igazítás) (blokk, oldalszám); fej \u003d getpage (ingyenes);) Void * tmp \u003d fej; fej \u003d (char *) fej + blokk; szabad - \u003d blokk; visszatérés TMP;) ~ Pointerbumpallocator () :: iterator i \u003d oldalak.begin (); I! \u003d Oldalak.end (); ++ i) (VirtualFree (* i, 0, mem_release);)))) Privát: Void * GetPage (Méret_t méret) (Void * Page \u003d VirtualAlloc (NULL, MÉRET, MEM_COMMIMIT | Mem_Rerve, Page_Readwrite); oldalak.push_back (oldal) ; Visszatérő oldal;) vektor Oldalak; Void * fej; SIZE_T INGYENES; ); Typedef Pointerbumpallocator<> Defaultáció;

Végül leírjuk a Childobject Adalékot a túlterhelt új és törléssel, amelyet a megadott alloctornak címeztünk:

Sablon. Struct Childobject (RETURN ALLOCATOR.ALLOCBLOCK (S);) statikus üresség * operátor új (RETURN ALLOCATOR-\u003e ALLOCBLOCK (S);) Statikus üres üzemeltető DELETE (VOID *, SIZE_T) () // * 1 statikus void operátor törlés (üresség) *, A *) () statikus üres üzemeltető törlés (Void *, A &) Privát: statikus üresség * Operátor új);););););

Ebben az esetben, amellett, hogy a gyermekosztályban lévő szennyeződés hozzáadása mellett szükség lesz arra is, hogy kijavítsa az új (vagy a gyári minta használatát). Az új operátorszintaxis a következő:

Új (... az üzemeltető paraméterei ...) Childobject (... tervező paraméterek ...)

A kényelem érdekében két új üzemeltetőt állítok be, amelyek A & VA vagy A *.
Ha az allocatort hozzáadják a szülőosztályhoz tagként, kényelmesebb az első lehetőséghez:
Csomópont \u003d új (alcator) xmlnode (nodename);
Ha az elosztót az őse (keverék) adja hozzá, akkor a második számára kényelmesebb:
Csomópont \u003d új (ez) xmlnode (nodename);

A törlési hívás nem rendelkezik speciális szintaxissal, a fordító szabványos törlést eredményez (* 1 jelzés), függetlenül attól, hogy melyik új állításokat használták egy objektum létrehozására. Ez az, hogy a szintaxis törlése normális:
CSOMÓPONT TÖRLÉSE;

Ha a Childobject tervező (vagy annak örököse) kivételével egy törlést hívnak az új üzemeltető aláírásának megfelelő aláírással, amely az objektum létrehozásakor használt új üzemeltető (az első size_t paraméter helyett ürül *).

Az új üzemeltető elhelyezése a magánszekcióban védi az új allost paramétert.

Teljes példát adok az allocator-childobject pár használatára:

Példa

xmldocument: Nyilvános defaultallocator (nyilvános: ~ xmldokument () ((vektor :: iterator i \u003d csomópontok.begin (); I! \u003d Csomópontok.end (); ++ i) (törlés (* i);)) Void addnode (char * tartalom, char * név) (char * c \u003d (char *) Allocblock (Strlen (tartalom) +1); strcpy (C, tartalom); char * n \u003d (char *) allocblock (strmlen (név) +1); strcpy (n, tartalom); csomópontok.push_back (új Xmlnode (C, N));) osztály Xmlnode: nyilvános gyermekbejám (Nyilvános: xmlnode (char * _content, char * _name): tartalom (_Content), név (_name) () privát: char * tartalom; char * név;); Privát: vektor csomópontok; );

Következtetés. A cikket 1,5 évvel ezelőtt írták a homokozóra, de sajnos, nem szerette a moderátort.

    a globális változókat és a konstansokat tárolja;

    a méretet összeállításkor határozzák meg.

    Stack (Stack)

    helyi változók tárolása, argumentumok és köztes számítási értékek;

    a méret meg van határozva, ha a program elindul (általában 4 MB-ot osztanak ki).

    Heap (halom)

    dinamikusan elosztott memória;

    Az OS kiemeli a memóriát az alkatrészekhez (szükség szerint).

Dinamikusan elosztott memóriát kell használni, ha előzetesen (egy program írásakor) nem tudjuk, hogy mennyi memória szükséges (például a tömb mérete attól függ, hogy a felhasználó belépjen a program során) és Nagy adatmennyiséggel dolgozik.

A dinamikus memória, más néven "halom", kifejezetten felosztása a program az erőforrásokból operációs rendszer és a mutató irányítja. Nem inicializálódott automatikusan, és egyértelműen ki kell engedni. A statikus és az automatikus memóriával ellentétben a dinamikus memória gyakorlatilag nem korlátozott (csak a méret véletlen hozzáférési memória) és változhat a program során

Dinamikus memóriával dolgozik

A nyelv dinamikus memóriájával való munkához a következő funkciókat használják: malloc, calloc, ingyen, realloc. Részletesebben fontolja meg őket.

    Kiválasztás (Memory Capture): Void * Malloc (Méret_t méret);

Bemeneti paraméterként a funkció a kiemelni kívánt memóriaszámot használja. A visszaküldött érték a memóriakártya mutatója egy halomban. Ha az operációs rendszer nem tudta elosztani a memóriát (például a memória nem volt elég), akkor a malloc visszatér 0.

    A munka vége után a kiosztott dinamikus memóriával meg kell szabadítani. Ebből a célból az ingyenes funkciót használják, amely visszaadja a memóriát az ellenőrzött operációs rendszerhez: Void Free (Void * PTR);

Ha a memória megjelent a program vége előtt, akkor automatikusan mentes a program befejezése után. Mindazonáltal a felesleges memória kifejezett megjelenése egy jó programozási stílus jele.

Példa: // memóriaelosztás 1000 típusú elemek alatt

int * p \u003d (int *) malloc (1000 * sizeof (int));

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

ingyenes (p); // A memória visszatérítése egy csomóba

2. Allocation (Memory Capture): Void * CALLOC (MÉRET_T NMEMB, MÉRET_T MÉRET);

A funkció hasonlóan működik a MALLOC-hez, de a szintaxis jellemzi (a memória méretének helyett), meg kell adnia az elemek számát és méretét), és a kiválasztott memória visszaállt. Például az INT * P \u003d (INT *) CALLOC (1000, SIMEF (INT)) P, P a P-t a nullákkal inicializált 1000 elemet tartalmazó INT típusú tömb elejére mutatja.

3. A memória méretének módosítása: Void * realloc (Void * PTR, MÉRET_T méret);

A funkció megváltoztatja a hozzárendelt memória méretét (ami jelzi ptr, A hívásból érkezett malloc, calloc vagy realloc). Ha a paraméterben megadott méret méret több, mint a mutató alatt kiemelt ptr, Ellenőrzött, hogy van lehetőség arra, hogy kiemelje a memória hiányzó sejtjeit egy sorban, már elkötelezett. Ha nincs elég hely, akkor megkülönböztetik a memória új részét méret és mutatóadatok pTR. Másoljon egy új webhely tetejére.

A program végrehajtásának folyamatában a dinamikus memóriahely mindenhol elérhető, ahol a mutató címe van ez a rész. Így a következő három lehetőség lehetséges, ha egy bizonyos egységben kiosztott dinamikus memóriával (például az egységes függvényben).

    A mutató (a dinamikus memóriahelyre) egy helyi automatikus memória objektumként van meghatározva. Ebben az esetben a kiválasztott memória nem érhető el, ha a helyblokk a helyblokkot adja ki, és ki kell engedni, mielőtt kilép a blokkból.

(int * p \u003d (int *) calloc (n, sizeof (int))

ingyenes (p); // DIN kiadás. memória

    A mutató helyi statikus memóriaként van meghatározva. A blokkban egyszer elosztott dinamikus memória egy mutatón keresztül elérhető a blokkban. A memóriát csak a használat végén szabadítani kell.

(Statikus int * p \u003d (int *) calloc (n, sizeof (int));

p \u003d (int *) calloc (n, sizeof (int));

f (50); // Dean Allocation. Memória a következő kiadásokkal

f1 (100); // Dean Allocation. Memória (első fellebbezés)

f1 (100); // dékánnal dolgozni. memória

f1 (0); // DIN kiadás. memória

    A mutató globális objektum a blokk tekintetében. A dinamikus memória minden olyan blokkban érhető el, ahol a mutató "látható". A memóriát csak a használat végén szabadítani kell.

iNT * PG; // Munka mutató a Dean számára. Memória (globális változó)

void init (int méret)

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

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

scanf ("% d", & pg [i]);

iNT összeg (int méret)

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

// memóriaelosztás

pg \u003d (int *) CALLOC (N, SIMEF (INT));

// DEAL PAM-val dolgozik

printf (\\ ns \u003d% d \\ n ", összeg (n));

ingyenes (pg); pg \u003d null; // A memória kiadása

Munka dinamikus memóriával C ++ -ban

A C ++ -ban van egy mechanizmus a memória elosztására és felszabadítására - ezek a funkciók. új és töröl.Példa a használatra új: int * p \u003d új int; // 1000 elrendelés alatt álló memória kiválasztása, azaz azaz A funkció használata esetén új Nem kell egy mutatót adni, és nem kell használni mérete (). Felszabadul új A memóriát a következő hívás követésével végezzük: P törlése; Ha az egyik elem memóriáját szeretné kiválasztani, akkor az INT * Q \u003d új int; vagy int * q \u003d új int (10); // A kiválasztott int az ebben az esetben a 10 értéket inicializálja, az eltávolítás így fog kinézni: q törlése;

vezetési idő programok. Helyi változók alatt a program memóriát vesz a veremterületről. A helyi változók azonban az egyes helyzetekre kiosztott memória mennyiségének előzetes meghatározását igénylik. Bár a C ++ hatékonyan hajtja végre az ilyen változókat, előzetesen meg kell ismernie a programozó számára, hogy minden helyzetben milyen mennyiségű memória szükséges.

A C ++ második módja a dinamikus eloszlás rendszerének használata. Ebben a módszerben a memóriát a szabad területről a szabad területről kell elosztani. A szabad memória terület a programkód között állandó memóriaterületével és veremével (24.1 ábra). A dinamikus szállás kényelmes, ha ismeretlen, hány adatelemet fognak feldolgozni.


Ábra. 24.1.

Mivel a program a verem területét használja le, azaz a program maga határozza meg a köteg memória térfogatát. Például egy nagyszámú program rekurzív funkciók több verm memóriát fog venni, mint egy olyan program, amely nem rendelkezik rekurzív funkciókMivel a helyi változók és a visszatérési címek tárolódnak a halomban. Memória a programhoz és globális változók kiemelkedik mindent vezetési idő A programok állandó jellegűek.

A program végrehajtásának folyamatában kiosztott memóriát dinamikusnak nevezik. A kiválasztás után dinamikus Elmentésre kerül az explicit felszabadításához, amelyet csak speciális műveletekkel vagy könyvtári funkcióval lehet végrehajtani.

Ha a program vége előtt a dinamikus memória nem jelenik meg, akkor a program befejezése után automatikusan mentes. Mindazonáltal a felesleges memória kifejezett megjelenése egy jó programozási stílus jele.

A program végrehajtásának folyamatában a dinamikus memóriahely mindenhol elérhető, ahol a mutató címe van ez a rész. Így a következők lehetségesek. három munkahelyi lehetőség dinamikus memóriávalegy bizonyos egységben (például a groár testben) szekretálódik.

  • A mutató (a dinamikus memóriahelyre) egy helyi automatikus memória objektumként van meghatározva. Ebben az esetben a kiválasztott memória nem érhető el, ha a helyblokk a helyblokkot adja ki, és ki kell engedni, mielőtt kilép a blokkból.
  • A mutató helyi statikus memóriaként van meghatározva. A blokkban egyszer elosztott dinamikus memória egy mutatón keresztül elérhető a blokkban. A memóriát csak a használat végén szabadítani kell.
  • A mutató globális objektum a blokk tekintetében. A dinamikus memória minden olyan blokkban érhető el, ahol a mutató "látható". A memóriát csak a használat végén szabadítani kell.

A programban bejelentett összes változó egy folyamatos memóriaterületre kerül adatszegmens. Az ilyen változók nem változtatják meg méretüket a program végrehajtása során, és hívják statikus. Az adatszegmens mérete nem elegendő ahhoz, hogy nagy mennyiségű információt helyezzen el. Ehhez a helyzetből való kilépés a dinamikus memória használata. Dinamikus memória - Ez a memória mennyiségét a program munkáját kevésbé adatszegmensben, a verem, amelyben a helyi változókat az alprogramok és a program maga találhatók.

A mutatók mutatókat használnak a dinamikus memóriával való munkához. Segítségükkel, hozzáférhet a dinamikus memóriaközpontokhoz dinamikus változók. Tárolásra dinamikus változók Egy speciális memóriaterületet kiemelnek, az úgynevezett "hibák".

Dinamikus változók Speciális funkciókkal és műveletekkel. Ezek a program végéig léteznek, vagy amíg a hozzájuk rendelt memória speciális funkciókat vagy műveleteket használ. Azaz az élettartam dinamikus változók - A program végéig a program végéig vagy egyértelműen engedje el a memóriát.

A C ++ két módot használ a dinamikus memóriával való munkavégzésre:

  1. a műveletek használata Új és Törlés;
  2. a MALCOS (CALLOC) FUNKCIÓK HASZNÁLATA (CREATITED C).

Dinamikus memóriával dolgozik az új és törlési műveletek használatával

A C ++ programozási nyelvben dinamikus memóriaelosztás Vannak új és törlés. Ezeket a műveleteket a memóriablokkok kiemelésére és kiadására használják. A memória területét, amelyben ezeket a blokkokat elhelyezik, hívják szabad memória.

Az új működési lehetővé teszi, hogy kiemelje, és hozzáférhető szabad telek a fő memória, amelynek méretei megfelelnek az adatok típusa határozza meg a típus neve.

Szintaxis:

Új nametype;

Új Nametype [inicializer];

A kiemelt területet a inicializálóami nem kötelező elem. Sikeres végrehajtás esetén az új visszaadja a kiválasztott memóriaterület megkezdésének címét. Ha a kívánt méretek szakaszát nem lehet kiválasztani (nincs memória), az új művelet visszaadja a nulla címértéket (NULL).

Szintaxis alkalmazás művelete:

Pointer \u003d Új NameType [inicializer];

Az új úszó művelet kiemeli a 4 bájt memóriaterületét. Az új INT (15) művelet kiemeli a 4 bájt memóriaterületét, és inicializálja ezt a webhelyet egy teljes értékkel 15. A hálózat használata A szintaxis és a törlés magában foglalja a mutatók használatát. Korábban minden mutatót be kell jelenteni:

típus * Névjelző;

Például:

float * pi; // a Pi Pi \u003d új úszó változó bejelentése; // a memória kiválasztása a PI * PI \u003d 2,25 változóhoz; // érték hozzárendelése

Mint például a típus, például a szabványos típusok használhatók. int, hosszú, float, dupla, char.

Az új üzemeltetőt leggyakrabban az adatok memóriájába tartozó típusspecifikus típusok fogadására használják, például struktúrák:

struktúra csomópont (char * név, int érték, csomópont * következő); Csomópont * Pnode; // bejelentette a PNODE \u003d új csomópont mutatót; // pnode-\u003e név \u003d "Ata" kiosztásra kerül; // hozzárendeli a PNODE-\u003e érték \u003d 1 értékeket; Pnode-\u003e következő \u003d null;

Név típusként új műveletben egy tömb használható:

Új Timassiva

Ha dinamikus memóriát rendel a tömbhöz, akkor a méreteket teljes mértékben meg kell határozni. Például:

pTR \u003d új int; // 10 típusú Int vagy 40 bájt eleme PTR \u003d új int; // helytelenül, mert Nem definiált méret

Ez a művelet lehetővé teszi, hogy kiemelje a terület dinamikus memóriában a megfelelő típusú tömböt, de nem teszi lehetővé, hogy inicializálható legyen. A végrehajtás eredményeként az új művelet visszaadja a mutatót, amelynek értéke a tömb első elemének címe. Például:

int * n \u003d új int;

Az új művelet dinamikus memória-típust hajt végre elegendő ahhoz, hogy az int, és rögzítse a terület elejének címét az N változóba. Az N változó alatti memória (mérete elegendő egy mutató elhelyezéséhez) a kompilációs szakaszban kiemelve.

Nagyon gyakran vannak feladatok feldolgozása adathordozó, amelynek dimenziója előre ismeretlen. Ebben az esetben két megközelítés egyikét lehet használni:

  • a memória kiválasztása egy statikus tömbhöz, amely tartalmazza a lehető legnagyobb számú elemet, de ebben az esetben a memória nem fogyasztott racionális;
  • dinamikus memóriaelosztás az adatgyűjtés tárolására.

A dinamikus memóriaelosztási funkciók használatához leírni kell a mutatót, amely a tömbelemek tárolásának kezdeti címe.

int * p; // mutató az int típushoz

A statikus tömb kezdeti címét a fordító határozza meg a bejelentés időpontjában, és nem módosítható.

Dinamikus tömb esetén a program végrehajtása során a kijelölt mutatóhoz a kezdeti címet hozzárendeli.

Standard dinamikus memóriaelosztási funkciók

A dinamikus memória funkciói a RAM-ban találhatók, a kívánt hosszúság folyamatos része, és visszaadja a terület kezdeti címét.

Dinamikus memóriaelosztási funkciók:

vOID * MALLOC (Méretezés);
vOID * CALLOC (számok, méret-elementavabytes);

A dinamikus memóriaelosztási funkciók használatához csatlakoztatnia kell a könyvtárat. :

#Inlude.

Mivel mindkét bemutatta, mint a visszaküldött értéket, mutató egy üres üregtípusra, a visszatérési érték típusának explicit kiemelkedése szükséges.

A malloc () függvény argumentumként használt bájtok méretének meghatározásához az elemek számát egy elemhez kell szorozni. Mivel a tömb elemei lehetnek mind az ilyen típusú adatok, mindössze kompozit típusok (például szerkezetek), hogy pontosan meghatározzák az elem méretét általában a funkció használatát

int sizeof (típus);


amely meghatározza a megadott típusú elem által elfoglalt byte mennyiségét.

A CALLOC (), MALLOC () függvények segítségével dinamikusan kiemelhető a memória

ingyenes (mutató);

A programozás során a "közös hangszabály" a dinamikusan elkülönített memória felszabadulása a további felhasználás hiányában. Ha azonban dinamikusan dedikált memória nem mentes, akkor a program végrehajtása befejeződik.

Dinamikus memóriaelosztás egydimenziós tömböknél

A keringés formája a tömb elemeihez a mutatók segítségével a következő űrlap:

int a, * p; // leírja a statikus tömböt és a mutatót
int b;
p \u003d a; // hozzárendelje a tömb kezdeti címét
... // belép a tömb elemeibe
b \u003d * p; // b \u003d a;
b \u003d * (p + i) // b \u003d a [i];

Példa C: Dinamikus egydimenziós tömb és elemek bevitele.

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


#Inlude.
#Inlude.
#Inlude.
int Main ()
{
int * a; // mutató egy tömb
int i, n;
Rendszer ("Chcp 1251");
Rendszer ("CLS");
Printf ( "Adja meg a tömb méretét:");
Scanf ("% d", & n);
// memóriaelosztás
a \u003d (int *) malloc (n * sizeof (int));
// belép a tömb elemeibe
mert (i \u003d 0; i {
Printf ("A [% d] \u003d", i);
Scanf ("% d", & A [I]);
}
// A tömb elemeinek megkötése
mert (i \u003d 0; i Printf ("% d", a [i]);
Ingyenes (a);
getchar (); getchar ();
vissza 0;
}


A program eredménye:

Dinamikus memóriaelosztás kétdimenziós tömböknél

Hagyjuk, hogy a dinamikus memóriában egy n húrok és m oszlopokat tartalmazó mátrix. A kétdimenziós mátrix a RAM-ban helyezkedik el, amely a húrok elemeiből álló szalag alakú. Ebben az esetben a kétdimenziós mátrix bármely elemének indexét a képlet segítségével lehet elérni

index \u003d I * M + J;

ahol az aktuális sorszám; J az aktuális oszlop száma.

Tekintsük a 3x4 mátrixot (lásd az ábrát)

A dedikált elem indexét úgy határozzák meg, hogy

index \u003d 1 * 4 + 2 \u003d 6

A kétdimenziós tömb befogadásához szükséges memória mennyiségét úgy határozzák meg, hogy

n · m · (elemméret)

Azóta azonban, hogy ezzel a nyilatkozattal a fordító egyértelműen nem jelzi egyértelműen a kétdimenziós tömb vonalon és oszlopában lévő elemek számát, az elem hagyományos fellebbezését a karakterlánc indexének és az oszlopindexének meghatározásával helytelen:

a [i] [j] - helytelen.

A mutató használatával a helyes fellebbezés úgy néz ki, mintha

* (P + I * M + J),
Hol

  • p egy tömb mutató
  • m - oszlopok száma,
  • i - vonalak index,
  • j az oszlop indexe.

Példa az si-re

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.
#Inlude.
#Inlude.
#Inlude.
int Main ()
{
int * a; // mutató egy tömb
int i, j, n, m;
Rendszer ("Chcp 1251");
Rendszer ("CLS");
Printf ( "Adja meg a sorok számát:");
Scanf ("% d", & n);
printf ();
Scanf ("% d", & m);
// memóriaelosztás
a \u003d (int *) malloc (n * m * sizeof (int));
// belép a tömb elemeibe
mert (i \u003d 0; i // sorciklus
{
mert (j \u003d 0; j // ciklus az oszlopokon
{
Scanf ("% d", (A + I * M + J);
}
}
// A tömb elemeinek megkötése
mert (i \u003d 0; i // sorciklus
{
mert (j \u003d 0; j // ciklus az oszlopokon
{
Printf ("% 5d", * (A + I * M + J);
}
Printf ("\\ n");
}
Ingyenes (a);
getchar (); getchar ();
vissza 0;
}

A végrehajtás eredménye

Egy másik módszer a dinamikus memória is lehetséges egy kétdimenziós tömb - egy sor mutató segítségével. Ehhez szüksége van:

  • válassza ki a RAM egységet a mutatók tömbben;
  • válassza ki a RAM blokkokat egydimenziós tömbökhez, amelyek a kívánt mátrix sorai;
  • jegyezze fel a sorok címét a mutatók tömbjéhez.

Grafikailag ilyen módszer a memóriakártás a következőképpen jeleníthető meg.


Ezzel a módszerrel a fordító mérete egyértelműen a sorok száma és az oszlopok száma a tömbben.
Példa az si-re

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.
#Inlude.
#Inlude.
#Inlude.
int Main ()
{
int ** a; // mutató a mutatóhoz az elemek vonalához
int i, j, n, m;
Rendszer ("Chcp 1251");
Rendszer ("CLS");
Printf ( "Adja meg a sorok számát:");
Scanf ("% d", & n);
Printf ( "Adja meg az oszlopok számát:");
Scanf ("% d", & m);
// a sorok memóriájának kiválasztása
// belép a tömb elemeibe
mert (i \u003d 0; i // sorciklus
{
// a tárolási húrok memóriájának kiválasztása
a [i] \u003d (int *) malloc (m * sizeof (int));
mert (j \u003d 0; j // ciklus az oszlopokon
{
Printf ("A [% d] [% d] \u003d", I, J);
Scanf ("% d", & A [I] [J]);
}
}
// A tömb elemeinek megkötése
mert (i \u003d 0; i< n; i++) // sorciklus
{
mert (j \u003d 0; j< m; j++) // ciklus az oszlopokon
{
Printf ("% 5d", a [i] [j]); // 5 Ismerkedés a tömb eleme alatt
}
Printf ("\\ n");
}
// tisztítási memória
mert (i \u003d 0; i< n; i++) // sorciklus
Ingyenes (a [i]); // a vonal memóriájának felszabadítása
Ingyenes (a);
getchar (); getchar ();
vissza 0;
}

A program eredménye hasonló az előző esethez.

A dinamikus memóriaelosztást a sor mutatók alatt szabad tömböket helyezhet el. A kétdimenziós tömb (mátrix) ingyenes, akinek a karakterlánc mérete eltérő lehet. A szabad tömb használatának előnye, hogy nem szükséges eltávolítani a számítógép memóriáját egy tartalékkal, hogy a karakterláncot a lehető legnagyobb hosszúságú. Tény, hogy a szabad tömb egydimenziós tömbje az egydimenziós adathordozókhoz.

Ahhoz, hogy a mátrixot a RAM-ban különböző hosszúságú vonalakkal kell elhelyezni, be kell írnia az M további tömböt, amelyben a húrok méretét tárolja.

Példa az si-re

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.
#Inlude.
#Inlude.
int Main ()
{
int ** a;
int i, j, n, * m;
Rendszer ("Chcp 1251");
Rendszer ("CLS");
Printf ( "Adja meg a sorok számát:");
Scanf ("% d", & n);
a \u003d (int **) malloc (n * sizeof (int *));
m \u003d (int *) malloc (n * sizeof (int)); // az elemek számának tömbje a
// belép a tömb elemeibe
mert (i \u003d 0; i {
Printf ( "Adja meg a% d oszlopok számát:", i);
Scanf ("% d", & m [i]);
a [i] \u003d (int *) malloc (m [i] * sizeof (int));
mert (j \u003d 0; j Printf ("A [% d] [% d] \u003d", I, J);
Scanf ("% d", & A [I] [J]);
}
}
// A tömb elemeinek megkötése
mert (i \u003d 0; i {
mert (j \u003d 0; j {
Printf ("% 3D", A [I] [J]);
}
Printf ("\\ n");
}
// A memória kiadása
mert (i \u003d 0; i< n; i++)
{
Ingyenes (a [i]);
}
Ingyenes (a);
Ingyenes (m);
getchar (); getchar ();
vissza 0;
}


A végrehajtás eredménye

A memória újraelosztása

Ha a memória memóriájának mérete nem adható meg előre, például amikor beírja az értékek sorrendjét egy adott parancshoz, akkor növelje a tömb méretét, amikor megadja a következő értéket, akkor végre kell hajtania A következő lépések:

  • Válassza ki az N + 1 méretegységét (1 több, mint a tömb aktuális mérete)
  • Másolja a tömbben tárolt értékeket egy újonnan dedikált memória területen.
  • A tömb tárolásához korábban elosztott szabad memória
  • Mozgassa a mutatót a tömb elindításához az újonnan kiválasztott memória terület elején.
  • Adjon hozzá egy sor utolsó bevezető értékét

A fenti lépések (az utolsó kivételével) végrehajtja a funkciót

vOID * REALLOC (VOID * PTR, SIZE_T méret);

  • a PTR egy mutató a korábban elosztott memória blokkjához, mely malloc (), CALLOC () funkcióval, vagy új helyre költözik. Ha ez a paraméter NULL, az új egység ki van osztva, és a funkció egy mutatót ad vissza.
  • a méret egy új méret, a kiosztott memóriablokk bájtján. Ha Size \u003d 0, korábban dedikált memória felszabadul, és a függvény egy zéró mutató, a PTR van telepítve NULL.

A memóriablokk mérete, amelyhez a PTR paraméter a méret bájtra változik. A memóriablokk csökkentheti vagy növelheti a méretét. A memóriablokk tartalma akkor is megmarad, ha az új egység kisebb méretű, mint a régi. De az új blokkon túlmutató adatok eldobják. Ha az új memóriablokk nagyobb, mint a régi, az újonnan kiosztott memória tartalma bizonytalan lesz.
ha (i\u003e 2) i - \u003d 2;
Printf ("\\ n");
a \u003d (int *) realloc (A, I * sizeof (int)); // a tömb méretének csökkentése 2
(int j \u003d 0; j)< i; j++)
Printf ("% d", a [j]);
getchar (); getchar ();
vissza 0;
}

A program két fő memóriájában tárolhatja a számítógép fő memóriájában. Az elsőnek a globális és helyi változókat használja, beleértve a tömböket, a struktúrákat és az osztályokat. A globális és statikus helyi változók esetében a tárolási hely a program összes végrehajtási idejére van rögzítve. Helyi változók esetében a memória kiemelve van a veremben. Bár a Borland C ++ -nél ezekkel a változókkal való együttműködés nagyon hatékonyan valósul meg, használatuk megköveteli, hogy a program végrehajtása során előzetesen ismerje meg a programozónak a programot.

Az információ tárolásának második módja a Borland C ++ memória dinamikus memóriájának rendszere. Ebben a módszerben az információk tárolására szolgáló memóriát a szükséges szabad területről kell osztani, szükség szerint, és visszaadja vissza, vagyis vissza. Ez akkor jelenik meg, ha szükség van az eltűnésre. A szabad memória terület a memória terület között helyezkedik el, ahol a program és a verem található. Ezt a területet egy csomó (halom) nevezik, és a dinamikus memóriaelosztás kérésére használják.

A dinamikus memória használatának előnye, hogy ugyanaz a memória használható különböző információk tárolására a program végrehajtási folyamatában. Mivel a memóriát egy adott célra osztják fel, és a felhasználás befejezése után felszabadul, ugyanazt a memóriát más időpontban használhatja más célokra a program egy másik részében. A dinamikus memóriaelosztás másik előnye a kapcsolódó listák, a bináris fák és más dinamikus adatstruktúrák létrehozása.

A nyelv memóriájának dinamikus memória elosztása a FUNC () és a szabad () funkciók, amelyek a szabványos könyvtár részei. Amikor a malloc () függvény, a memória elosztásához memória érkezik, a rendelkezésre álló szabad memória egy része megkülönböztethető. Ha ez a memória az ingyenes () függvény használatával jelenik meg, akkor ez a memória visszaadja a rendszert.

A C ++ nyelv két dinamikus memóriaelosztási szolgáltatót határoz meg - új és törlés.

Az ANSI C szabvány csak négy dinamikus memóriaelosztási funkciót határoz meg: CALLOC (), MALLOC (), INGYENES () és REALLOC (). A Borland C ++ azonban számos más dinamikus memóriaelosztási funkciót tartalmaz. A modern 32 bites memória modell kódjának összeállításakor a memória lapos, és általában csak négy szabványos memóriaelosztási funkciót használ.

Az ANSI C szabvány határozza meg, hogy a dinamikus memóriaelosztáshoz szükséges fejléc információ az STDLIB.H fájlban található. A Borland C ++ azonban lehetővé teszi a stdlib.h vagy az alloc.h fejléc fájlokat. Itt használjuk a STDLIB.H fejlécfájlt, mivel hordozhatóságot biztosít. Egyes más dinamikus memóriaelosztási funkciók Alloc.h fejlécfájlokat, malloc.h vagy dos.h. Különös figyelmet kell fordítani arra, hogy mely fejléc fájlra van szükség az egyes funkciók használatához.



Tetszett a cikket? Oszd meg