A pointerek után nézzük a dinamikus memóriakezelést. Ez a dolog onnan kezdődik, hogy memóriát foglalunk magunknak, és erre egy pointerrel hivatkozunk. Kezdjük ott, hogy mennyi memóriát foglalhatunk, és hol. Ez mindig a memóriamodelltől függ. A small és tiny modellekben a rendelkezésünkre álló terület az adatszegmens végétől a stack tetejéig tart. Egy vékony sávot kivéve a stack előtt. Ez persze kisebb, mint 64k. A compact large és huge modelleknél a lefoglalható terület a stack végétől a memória végéig tart.

Mi maradjunk csak SMALL-ban. Ekkor az összes adat maximum egy szegmens lehet. Tehát a statikus, és a dinamikus együtt 64k lehet. Hogy mire jó? Segítségével, mint ahogy az nevéből is következik, futási idő alatt dinamikusan hozhatunk létre változókat. Ez azért előnyös, mert bizonyos esetekben nem tudhatjuk előre, pl. egy tömb, egy karakter, vagy egy láncolt lista elemszámát, ezért a statikus memóriakezelés teljesen csődöt mond. Arról nem is beszélve, hogy pl. egy statikusan kezelt globális hosszú tömb a program egész futási ideje alatt ott foglalja a memóriát, míg egy dinamikusan lefoglalt területet bármikor felszabadíthatunk, ha már nem kell. Ennyi elméletileg.

Gyakorlatilag: használhatjuk olyan tömbök létrehozására, melynek elemszáma futási időben derül ki. pl.: file beolvasás. Segítségével megvalósíthatunk bizonyos logikai adatszerkezeteket. pl.: sor, verem, bináris fa ... És utoljára de nem utolsósorban (habár ez már nem az a klasszikus dinamikus memóriakezelés) kiterjeszthetjük az adatméretet a memóriamodellben maximált fölé, ha nem a heap-ből foglalunk, hanem a DOS-tól. Ugyanis a C a Pascallal ellentétben nem foglalja le az összes memóriát ahogy a program elindul, csak annyit, amennyi az adott memóriamodell kezel. Így aztán small módban jó megközelítéssel van még 400k DOS memóriánk, amit dinamikus kezeléssel igen könnyen kihasználhatunk (persze csak szegmensenként), nélküle azonban sehogy.

Egy pár függvény, amire szükségünk lesz a malloc.h-ból:

void near *malloc(unsigned s);

A heapből lefoglal s byte-nyi helyet, és a lefoglalt terület címét adja visszatérési értékként. Ha nincs elegendő hely, NULL-al tér vissza. A párja:

int free(void near *p);

A malloc()-al lefoglalt p címen kezdődő területet szabadítja fel.

Ha large compact vagy huge modellben vagyunk valószínűleg far pointerekkel dolgozunk, ekkor azonban az előbbi két függvény nem fog működni. Ekkor a farmalloc() és farfree() fügvényeket használhatjuk. Sőt, ha huge pointereket használunk, a lefoglalt memóriaterület túllépheti az egy szegmenset.

A DOS memória kezelésében a következő két függvényt fogjuk használni a DOS.H-ból:

int allocmem(unsigned size,int *seg);

Size*16 byte-ot foglal le a DOS-tól, majd a kezdő szegmenset beteszi *seg-be. Ha a foglalás sikeres nulla a visszatérési érték, ha -1, akkor sikertelen, ekkor a legnagyobb lehetséges blokk mérete lesz seg-ben.

int freemem(int seg);

az allocmem() által lefoglalt memóriát szabadítja fel.

Akkor nézzünk egy pár példát. Eddig a rendezésnél a rendezendő elemek száma 15-re volt korlátozva, ugyanis ennyi volt a tömbünk hossza. Lássuk a megoldást:

#include <STDIO.H>
#include <STDLIB.H>
#include <MALLOC.H>
void main(void)
{
int a,n,i,j,cs,x;
int *tomb;
char string[255];
printf("Mennyi elemet rendezzek?\n");
do
{
gets(string);
n=atoi(string);
} while (tomb=(int *)malloc(size(int)*n)); //lefoglalunk
for(a=0;a<n;a++) // innen minden a régi 
{
printf("kerem a(z) %d. elemet\n",a+1);
gets(string);
tomb[a]=atoi(string);
}
i=n-1;
while(i>=1)
{
cs=0;
for(j=0;j<i;j++)
if (tomb[j]>tomb[j+1])
{
cs=j;
x=tomb[j+1];tomb[j+1]=tomb[j];tomb[j]=x;
}
i=cs;
}
for(a=0;a<n;a++)
{
printf("a(z) %d. elem: %d\n",a+1,tomb[a]);
}
free(tomb); //felszabadulás
}

Azt már az előző szám óta tudjuk, hogy egy szál pointert nyugodtan kezelhetünk tömbként. Az új a malloc() függvény használata volt. Vele tudunk a heap-ből memóriát foglalni. Egyedül a lefoglalandó byte-ok számát kel megadni paraméterként, és a lefoglalt memóriaterület kezdőcímével (egy pointer) tér vissza. Már ha sikerült lefoglalnia, egyébként NULL. Azt hiszem némi magyarázatra szorul az-az, (int *) a függvényhívás előtt. Nos ezt nevezik Cast-olásnak. Bővebben majd később foglalkozok vele, nagyjából annyit tesz, hogy megmondja a fordítónak a mögötte álló kifejezés típusát. Tehát azt mondjuk a fordítónak: figyelj öreg ez nem akármilyen, hanem, egy int-re mutató pointer ám. Ha ezt nem tennénk a compiler valami can't convert void * to int * hibaüzenettel dobna meg minket. Mivelhogy a szerencsétlen convertálni akar. Ezért aztán ehhez hasonlóan akárhányszor egymással nem megegyező típusú pointereket akarunk egymással megfeleltetni, castolnunk kell. Ami még új lehet, az a size() használata. Ez a függvény megadja a paraméterként megadott típus méretét byte-okban. Használatára azért van szükség, mert a malloc()-nak byte-okban kell megadni a lefoglalandó terület hosszát. 

A logikai adatszerkezetek megvalósítását a következő számra hagynám, mivelhogy így egyszerre el tudom (remélem) magyarázni a struktúrákat is.