Dinamikus memóriakezelés
A C nyelv egyik legnagyobb erőssége, hogy lehetőséget ad a programozónak az adatok memóriaigényének rugalmas kezelésére. Ezzel a lehetőséggel optimalizálhatjuk a programunkat, ami a gyorsaság szempontjából fontos. A lényeg persze itt is szemléltetés, mivel a memóriakezelés valódi előnyei az itt bemutatottaknál jóval robusztusabb programoknál tapasztalhatók meg.
Az alábbi program karakterláncokat kér be a felhasználótól. Ezután kiíratja a begépelt karakterláncokat (nevek,szavak) a konzolra. A bekérést a getline függvény végzi, ami új sorig (‘\n’, ENTER) olvassa be a karaktereket. A main függvényben a getline egészen addig kerül meghívásra, amíg a felhasználó üres sort nem üt vagy amíg el nem éri a maximálisan megadható karakterláncot (n=5). A program az érvényes karakterláncoknak annyi bájtot foglal a memóriában amilyen hosszúak [strlen(input)+1], ebbe beletartozik a lánczáró ‘\0’ is, ami a C nyelvben a karakterláncokat azonosítja (erre utal a +1). A program a karakterláncokat egy kétdimenziós tömbben helyezi el, végül pedig felszabadítja a lefoglalt memóriát, amikor már nincs rá szükség.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <windows.h>
#define BUF 256
int getline(char s[], int lim) {
int c, i;
for (i = 0; i < lim && (c = getchar()) != EOF && c != '\n'; ++i)
s[i] = c;
s[i] = '\0';
while (c != EOF && c != '\n')
getchar();
return(i);
}
int main() {
setlocale(LC_ALL,"Hungarian_Hungary.1250");
char **str, input[BUF];
int n = 5, i = 0;
//memória foglalás: hány karakterláncra lesz szükség
str = malloc(sizeof(char*)*n);
printf("Adjon meg karakterláncokat!\n");
while (getline(input, BUF)) {
/*
validáló függvény helye
*/
//memória foglalás: hány karakterből fog állni
str[i] = malloc(strlen(input) + 1);
strcpy(str[i], input);
i++;
if (i == n)
break;
}
if (i < n) //ha üres sorral korábban ugrik ki
n = i;
//teszt
printf("A megadott karakterláncok:\n");
for (i = 0; i < n; i++)
printf("%s\n", str[i]);
//memória felszabadítás
for (i = 0; i < n; i++)
free(str[i]);
free(str);
return 0;
}
Ahogy látható két memória foglalás is történt. Mind a kettő a malloc, memória allokációt végző függvénnyel. A malloc első meghívásával a karakterláncok várható számának foglaltunk memóriát (**str). Esetünkben legfeljebb öt karakterláncra kellett felkészülni. A malloc második hívásakor az érvényes karakterláncoknak foglaltunk helyet a memóriában (str[i]) (megj.: a program minden karakterláncot érvényes bementként fogad el, mivel az input ellenőrzésétől eltekintettünk. Példák a jegyzetben).
A következő program egy megadott szöveges fájlból olvassa be a karakterláncokat, az fbuf függvény meghívásával. Karakterláncokról lévén szó vagyis karakterek tömbjéről, ezért a tárolásuk hasonlóan az előző programhoz szintén kétdimenziós tömbökben történik, azzal a különbséggel, hogy a sorok függvény segítségével tudni fogjuk hány adat számára kell memóriát foglalni.
Miután az egyes karakterláncok foglalása is megtörtént, az adatokat buborék rendezéssel ábécé sorrendbe állítjuk, majd kiíratjuk a konzolon. A program a visszatérés előtt felszabadítja a memóriát és bezárja a fájlt.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF 256
int sorok(FILE *fp) {
int sor = 0, aktpoz = ftell(fp);
char *fbuf = malloc(BUF);
fseek(fp, 0L, SEEK_SET);
while (fgets(fbuf, BUF, fp) != NULL)
sor++;
fseek(fp, aktpoz, SEEK_SET);
free(fbuf);
return sor;
}
int main() {
FILE *fp = fopen("teszt.txt","r");
if (!fp) {
perror("");
return fp;
}
char fbuf[BUF], **adat, *temp;
int sorsz = sorok(fp);
adat = malloc(sizeof(char*)*sorsz);
//adatok tömbbe mentése - egy sor egy adatot tartalmaz
int m = 0;
while (fgets(fbuf, BUF, fp) != NULL) {
fbuf[strlen(fbuf) - 1] = '\0';
adat[m] = malloc(strlen(fbuf) + 1);
strcpy(adat[m], fbuf);
m++;
}
//ábécé sorrend
for (int i = 0; i < m; i++)
for (int j = i + 1; j < m; j++) {
if (strcmp(adat[i], adat[j]) > 0) {
temp = malloc(strlen(adat[i]) + 1);
strcpy(temp, adat[i]);
adat[i] = realloc(adat[i], strlen(adat[j]) + 1);
strcpy(adat[i], adat[j]);
adat[j] = realloc(adat[j], strlen(temp) + 1);
strcpy(adat[j], temp);
free(temp);
}
}
//rendezett sorrend kiíratása
for (int i = 0; i < m; i++) {
printf("%d.%s\n", i + 1, adat[i]);
free(adat[i]);
}
free(adat);
fclose(fp);
return 0;
}
Az ábécé sorrendbe rendezésnél vannak optimálisabb megoldások is, ugyanakkor reményeim szerint a realloc függvény használatának bemutatására ez a kód is alkalmas lehet. Ebben az esetben arról van szó, hogy a buborék rendezésnél ideiglenes (temp) karaktertömböt hozunk létre, melyet mindig az aktuális összehasonlításnál veszünk igénybe, ez azonban azzal az igénnyel is járhat, hogy az éppen megfelelő méretet különítsük el a memóriában. A kódban látható, hogy realloc első argumentuma a módosítandó tömb neve, a második pedig az új méret. A realloc függvény tehát nem csinál mást, minthogy újraméretezi a lefoglalandó memóriát a karakterlánc számára.
Számoknak is hasonlóképpen tudunk memóriát foglalni, így például egészeket tartalmazó táblázatoknak, mátrixoknak. Az alábbi programban a mátrix sora (M) és oszlopa (N) is definiált vagyis nem futásidőben kapott érték (5×3). A mátrixhoz tarozó számokat is a program generálja: ( j+i). A kód után a mátrix konzolra nyomtatott képe látható.
#include <stdio.h>
#include <stdlib.h>
#define M 5
#define N 3
int main() {
int **arr, i, j;
//memória foglalás
arr = malloc(sizeof(int*) * M); //sorok
for (i = 0; i < M; i++)
arr[i] = malloc(sizeof(int) * N); //oszlopok
//értékadás, kiíratás
for (i = 0; i < M; i++) {
for (j = 0; j < N; j++) {
arr[i][j] = j + i;
printf("%d ", arr[i][j]);
}
printf("\n");
}
//memória felszabadítás
for (i = 0; i < M; i++)
free(arr[i]);
free(arr);
return 0;
}
