Dinamikus memóriakezelés

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.

 

Karakterláncok bekérése dinamikus memóriafoglalással

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).

 

Memória újrafoglalása – realloc()

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.

 

Dinamikus memóriafoglalás egész számoknak – Mátrix

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;
}

Példa mátrixra