Dinamikus memóriafoglalás C-ben: malloc(), calloc()

⚡ Okos összefoglaló

A C dinamikus memóriafoglalása futásidőben lefoglalja és felszabadítja a heap memóriát az stdlib.h könyvtár malloc(), calloc(), realloc() és free() függvényeivel, lehetővé téve a programok számára, hogy rugalmasan méretezzék a tömböket és a struktúrákat, amikor a követelmények a végrehajtás előtt ismeretlenek.

  • 🧠 Stack vs. heap: Az alapváltozók az automatikus veremtárolót használják, míg a dinamikus memória a heap-en található, és manuálisan, mutatókon keresztül kezelhető.
  • 📦 malloc(): Lefoglal egy inicializálatlan blokkot a kért bájtméretből, és egy void pointert, vagy NULL-t ad vissza, ha nincs elég memória.
  • 🔢 calloc(): Több blokkot foglal le a tömbökhöz, minden bájtot nullára állít, és egy mutatót ad vissza az első blokkra.
  • 🔄 realloc(): Egy meglévő blokk méretét növeli vagy csökkenti, miközben megőrzi az eredeti tartalmat egy esetleges új helyen.
  • 🧹 ingyenes(): Memóriát ad vissza a heap-nek; minden malloc() vagy calloc() függvényhez szüksége van egy megfelelő free()-re a szivárgások elkerülése érdekében.
  • 🤖 MI segítség: A mesterséges intelligencia asszisztensei és a GitHub Copilot elkészítik az allokációs kód vázlatát, és jelzik a hiányzó free() hívásokat és szivárgásokat.

Dinamikus memóriafoglalás C-ben

Hogyan működik a memóriakezelés C-ben?

Amikor egy változót alapvető adattípussal deklarálunk, a C fordító automatikusan lefoglalja a változó számára a memóriaterületet egy veremnek nevezett memóriaterületen.

Például egy float változó deklarálása jellemzően 4 bájtot vesz igénybe (platformtól függően). Ezt az információt a sizeof operátorral ellenőrizhetjük, ahogy az az alábbi példában látható.

#include <stdio.h>
int main() { float x; printf("The size of float is %d bytes", sizeof(x)); return 0;}

A kimenet a következő lesz:

 The size of float is 4 bytes

Ezenkívül egy meghatározott méretű tömb összefüggő memóriablokkokban van lefoglalva, minden blokknak egy elem mérete van:

#include <stdio.h>
int main() { float arr[10];
printf("The size of the float array with 10 element is %d", sizeof(arr)); return 0;}

Az eredmény:

 The size of the float array with 10 element is 40

Ahogy eddig is tanultuk, egy alap adattípus vagy tömb deklarálásakor a memóriakezelés automatikusan történik. Létezik azonban egy memóriafoglalási folyamat C-ben, amely lehetővé teszi olyan program megvalósítását, amelyben a tömb mérete eldöntetlen a program futtatásáig (futásidejű). Ezt a folyamatot „dinamikus memóriafoglalásnak” nevezik.

Dinamikus memóriafoglalás C-ben

A dinamikus memória-elosztás a memória manuális lefoglalása és felszabadítása a programozási igényeknek megfelelően. A dinamikus memóriát olyan mutatókkal kezeljük és szolgáljuk ki, amelyek az újonnan lefoglalt memóriaterületre mutatnak egy olyan területen, amelyet halomnak nevezünk.

Mostantól probléma nélkül létrehozhat és megsemmisíthet egy elemtömböt dinamikusan futás közben. Összefoglalva, az automatikus memóriakezelés a verem, a C Dynamic Memory Allocation pedig a kupacot használja.

A A könyvtár a dinamikus memóriakezelésért felelős funkciókkal rendelkezik.

Funkció Cél
malloc () Lefoglalja a kívánt méretű memóriát, és visszaadja a mutatót az első bájtjára
kiosztott hely.
calloc() Lefoglalja a helyet egy tömb elemei számára. Nullára inicializálja az elemeket, és visszaad egy mutatót a memóriába.
realloc() A korábban lefoglalt memóriaterület méretének módosítására szolgál.
Ingyenes() Felszabadítja vagy kiüríti a korábban lefoglalt memóriaterületet.

Beszéljük meg a fenti függvényeket az alkalmazásukkal együtt

malloc() függvény C-ben

A C malloc() függvény a memóriafoglalást jelenti. Ez egy memóriablokk dinamikus lefoglalására szolgáló funkció. Meghatározott méretű memóriaterületet foglal le, és visszaadja a memóriahelyre mutató nullmutatót. A visszaadott mutató általában void típusú. Ez azt jelenti, hogy bármelyik mutatóhoz hozzárendelhetjük a C malloc() függvényt.

A malloc() függvény szintaxisa:

ptr = (cast_type *) malloc (byte_size);

Itt,

  • A ptr egy cast_type mutató.
  • A C malloc() függvény egy mutatót ad vissza a byte_size lefoglalt memóriájába.

Példa a malloc()-ra:

Example: ptr = (int *) malloc (50)

Ha ez az utasítás sikeresen végrehajtódik, 50 bájtos memóriaterület kerül lefoglalásra. A lefoglalt terület első bájtjának címe az int típusú ptr mutatóhoz van hozzárendelve.

Vegyünk egy másik példát:

#include <stdlib.h>
int main(){
int *ptr;
ptr = malloc(15 * sizeof(*ptr)); /* a block of 15 integers */
    if (ptr != NULL) {
      *(ptr + 5) = 480; /* assign 480 to sixth integer */
      printf("Value of the 6th integer is %d",*(ptr + 5));
    }
}

output:

Value of the 6th integer is 480

C malloc() függvény

  1. Figyeljük meg, hogy a sizeof(*ptr) függvényt használtuk a sizeof(int) helyett, hogy a kód robusztusabb legyen, amikor a *ptr deklarációt később más adattípusra tipizáljuk.
  2. A kiosztás sikertelen lehet, ha a memória nem elegendő. Ebben az esetben NULL mutatót ad vissza. Tehát a NULL mutató ellenőrzéséhez kódot kell megadnia.
  3. Ne feledje, hogy a lefoglalt memória összefüggő, és tömbként kezelhető. A tömbelemek eléréséhez használhatunk mutató aritmetikát, nem pedig zárójeleket [ ]. Javasoljuk, hogy a + használatával hivatkozzon a tömbelemekre, mert a ++ vagy += növelés megváltoztatja a tömb által tárolt címet. mutató.

A Malloc() függvény karakteres adattípusokkal, valamint összetett adattípusokkal, például struktúrákkal is használható.

free() függvény C-ben

A memória a változók fordításkor automatikusan felszabadításra kerül. A dinamikus memóriafoglalásnál kifejezetten fel kell szabadítania a memóriát. Ha ez nem történik meg, memóriahiány léphet fel.

A free() függvény meghívásával felszabadíthatjuk/felszabadíthatjuk a memóriát a C nyelvben. A programban a memória felszabadításával több szabadíthatunk fel későbbi felhasználásra.

Például:

#include <stdio.h>
int main() {
int* ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL){
  *(ptr + 2) = 50;
  printf("Value of the 2nd integer is %d",*(ptr + 2));
}
free(ptr);
}

output:

 Value of the 2nd integer is 50

calloc() függvény C-ben

A C calloc() függvény a szomszédos allokációt jelöli. Ez a funkció több memóriablokk lefoglalására szolgál. Ez egy dinamikus memóriafoglalási funkció, amely a memória komplex adatstruktúrákhoz, például tömbökhöz és struktúrákhoz való hozzárendelésére szolgál.

A Malloc() függvény egyetlen memóriablokk lefoglalására szolgál, míg a calloc() C-ben több memóriablokk lefoglalására szolgál. A calloc() függvény által lefoglalt minden blokk azonos méretű.

A calloc() függvény szintaxisa:

ptr = (cast_type *) calloc (n, size);

A fenti utasítás n azonos méretű memóriablokk lefoglalására szolgál.

A memóriaterület lefoglalása után az összes bájt nullára inicializálódik.

Az aktuálisan lefoglalt memóriaterület első bájtjánál lévő mutató kerül visszaadásra.

Ha hiba történik a memóriaterület lefoglalásakor, például memóriahiány, nulla mutatót ad vissza.

Példa a calloc()-ra:

Az alábbi program egy számtani sorozat összegét számítja ki.

#include <stdio.h>
    int main() {
        int i, * ptr, sum = 0;
        ptr = calloc(10, sizeof(int));
        if (ptr == NULL) {
            printf("Error! memory not allocated.");
            exit(0);
        }
        printf("Building and calculating the sequence sum of the first 10 terms \ n ");
        for (i = 0; i < 10; ++i) { * (ptr + i) = i;
            sum += * (ptr + i);
        }
        printf("Sum = %d", sum);
        free(ptr);
        return 0;
    }

Eredmény:

Building and calculating the sequence sum of the first 10 terms
Sum = 45

calloc() vs. malloc(): Kulcskülönbségek

Az alábbiakban bemutatjuk a legfontosabb különbséget malloc() vs calloc() C-ben:

A calloc() függvény általában alkalmasabb és hatékonyabb, mint a malloc() függvény. Míg mindkét függvény memóriaterület lefoglalására szolgál, a calloc() egyszerre több blokkot is lefoglalhat. Nem kell minden alkalommal memóriablokkot kérni. A calloc() függvényt összetett adatstruktúrákban használják, amelyek nagyobb memóriaterületet igényelnek.

A calloc() által lefoglalt memóriablokk C-ben mindig nullára van inicializálva, míg a malloc() függvényben C-ben mindig tartalmaz egy szemét értéket.

realloc() függvény C-ben

A C realloc() függvénnyel növelheted a már lefoglalt memória méretét. Kibővíti az aktuális blokkot, miközben az eredeti tartalmat változatlanul hagyja. A C-ben a realloc() a memória újrafoglalását jelenti.

A realloc() a korábban lefoglalt memória méretének csökkentésére is használható.

A realloc() függvény szintaxisa:

ptr = realloc (ptr,newsize);

A fenti utasítás egy új memóriaterületet foglal le meghatározott mérettel a newsize változóban. A funkció végrehajtása után a mutató visszakerül a memóriablokk első bájtjába. Az új méret lehet nagyobb vagy kisebb, mint az előző memória. Nem lehetünk biztosak abban, hogy az újonnan lefoglalt blokk ugyanarra a helyre fog mutatni, mint az előző memóriablokk. Ez a funkció az összes korábbi adatot átmásolja az új régióba. Biztosítja, hogy az adatok biztonságban maradjanak.

Példa a realloc()-ra:

#include <stdio.h>
int main () {
   char *ptr;
   ptr = (char *) malloc(10);
   strcpy(ptr, "Programming");
   printf(" %s,  Address = %u\n", ptr, ptr);

   ptr = (char *) realloc(ptr, 20); //ptr is reallocated with new size
   strcat(ptr, " In 'C'");
   printf(" %s,  Address = %u\n", ptr, ptr);
   free(ptr);
   return 0;
}

Amikor a realloc() C-ben sikertelen műveletet eredményez, null mutatót ad vissza, és a korábbi adatok is felszabadulnak.

Dinamikus tömbök C-ben

A C dinamikus tömbje lehetővé teszi az elemek számának igény szerinti növekedését. C A dinamikus tömböt széles körben használják a számítástechnikai algoritmusokban.

A következő programban létrehoztunk és átméreteztünk egy dinamikus tömböt C

#include <stdio.h>
    int main() {
        int * arr_dynamic = NULL;
        int elements = 2, i;
        arr_dynamic = calloc(elements, sizeof(int)); //Array with 2 integer blocks
        for (i = 0; i < elements; i++) arr_dynamic[i] = i;
        for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
        elements = 4;
        arr_dynamic = realloc(arr_dynamic, elements * sizeof(int)); //reallocate 4 elements
        printf("After realloc\n");
        for (i = 2; i < elements; i++) arr_dynamic[i] = i;
        for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
        free(arr_dynamic);
    }

A C Dynamic array program eredménye a képernyőn:

 
arr_dynamic[0]=0
arr_dynamic[1]=1
After realloc
arr_dynamic[0]=0
arr_dynamic[1]=1
arr_dynamic[2]=2
arr_dynamic[3]=3

GYIK

Memóriaszivárgás akkor történik, amikor a malloc() vagy calloc() által elfoglalt heap memória soha nem szabadul fel. Ezt úgy előzhetjük meg, hogy minden lefoglalást egy megfelelő free() hívás kísér, miután a blokkra már nincs szükség.

Egy lelógó mutató továbbra is a free() által már felszabadított memória címét tartalmazza. A dereferenciálása definiálatlan viselkedést okoz, ezért a biztonság kedvéért a felszabadítás után azonnal rendeljünk NULL értéket a mutatóhoz.

Nem. A C nyelvben nincs szemétgyűjtő, így a heap memória soha nem ürül ki automatikusan. A programozónak minden blokkot explicit módon fel kell szabadítania a free() függvénnyel; ellenkező esetben a lefoglalás a program leállásáig megmarad.

Nem. A free() függvény kétszeri meghívása egy címen dupla free()-et eredményez, és nem definiált viselkedést vagy összeomlást vált ki. Ha a mutatót NULL-ra állítjuk az első free() függvény után, akkor minden későbbi free() függvény ártalmatlanná válik.

A realloc(NULL, size) pontosan úgy működik, mint a malloc(size), és egy új blokkot foglal le. A realloc(ptr, 0) felszabadítja a blokkot, tehát mindig a visszaadott mutatót rögzíti az eredeti felülírása helyett.

Eszközök, mint például a Valgrind, az AddressSanitizer és a Dr. Memory track minden olyan allokációs és jelentésblokkot, amely soha nem lett felszabadítva, a pontos sorral együtt, helping a kiadás előtt megtalálja a szivárgásokat.

Igen. Egy mesterséges intelligencia által létrehozott kódolási asszisztens malloc(), calloc() és free() szekvenciákat rajzol ki egy promptból, elmagyarázza a mutatóhibákat, és jelzi a hiányzó free() hívásokat. Mindig fordítsd le és teszteld a generált kódot, mielőtt megbízol benne.

GitHub másodpilóta allokációs blokkokat, NULL ellenőrzéseket és a free() hívások egyeztetését javasolja egy megjegyzésből; minden kiegészítést ellenőrizni kell a szivárgások és a helyes méretek szempontjából, mielőtt elküldenéd.

Foglald össze ezt a bejegyzést a következőképpen: