Hvordan virker malloc? Minnetildeling i C (malloc-funksjon)

Hver gang pekeren ble initialisert, ble adressen til en eller annen variabel brukt. Dette var på grunn av det faktum at C++ språkkompilatoren automatisk allokerer minne for lagring av variabler og ved hjelp av en peker kan du jobbe med dette tildelte området uten konsekvenser. Imidlertid er det malloc() og free() funksjoner som lar deg tildele og frigjøre minne etter behov. Disse funksjonene er plassert i biblioteket og har følgende syntaks:

void* malloc(størrelse_t); //minnetildelingsfunksjon
void free(void* memblock); //minnefrigjøringsfunksjon

Her er size_t størrelsen på det tildelte minneområdet i byte; void* er en generisk pekertype, dvs. ikke knyttet til noen spesifikk type. La oss se på hvordan disse funksjonene fungerer ved å bruke eksemplet med å tildele minne for 10 elementer av dobbel type.

Oppføring 4.3. Programmere en dynamisk array.

#inkludere
#inkludere
int main()
{
dobbel*ptd;
ptd = (dobbel *)malloc(10 * sizeof(dobbel));
if(ptd != NULL)
{
for(int i = 0;i ptd[i] = i;
) else printf(“Kunne ikke tildele minne.”);
gratis(ptd);
returner 0;
}

Når malloc()-funksjonen kalles, beregnes det nødvendige minneområdet for å lagre 10 doble elementer. For å gjøre dette, bruk sizeof()-funksjonen, som returnerer antall byte som kreves for å lagre ett dobbeltelement. Deretter multipliseres verdien med 10 og resultatet er volumet for 10 doble elementer. I tilfeller der den angitte mengden minne av en eller annen grunn ikke kan tildeles, returnerer malloc()-funksjonen NULL. Denne konstanten er definert i flere biblioteker, inkludert og. Hvis malloc()-funksjonen returnerte en peker til det tildelte minneområdet, dvs. ikke lik NULL, så utføres en løkke hvor verdiene for hvert element er skrevet. Når programmet avsluttes, kalles free()-funksjonen, som frigjør det tidligere tildelte minnet. Formelt sett frigjør et program skrevet i C++ ved fullføring automatisk alt tidligere tildelt minne, og free()-funksjonen, i dette tilfellet, kan utelates. Men når du skriver mer komplekse programmer, må du ofte allokere og deallokere minne mange ganger. I dette tilfellet spiller free()-funksjonen en stor rolle, fordi Minne som ikke frigjøres kan ikke gjenbrukes, noe som vil resultere i unødvendig sløsing med dataressurser.

Bruken av pekere ble "arvet" fra språket C. For å forenkle prosessen med å endre parametere, introduserer C++ konseptet med en referanse. En referanse er et alias (eller andrenavn) som programmer kan bruke for å referere til en variabel. For å erklære en kobling i et program, bruk &-tegnet foran navnet. Det særegne ved å bruke lenker er behovet for å initialisere dem umiddelbart etter erklæring, for eksempel:

int var;
int &var2 = var;

Her er en referanse kalt var2 deklarert, som initialiseres av variabelen var. Dette betyr at var-variabelen har sitt eget alias var2, der enhver endring i verdiene til var-variabelen er mulig. Fordelen med å bruke referanser fremfor pekere er at de må initialiseres, så programmereren er alltid sikker på at var2-variabelen fungerer med et tildelt minneområde og ikke med et vilkårlig, noe som er mulig ved bruk av pekere. I motsetning til pekere, initialiseres en referanse bare én gang, når den er deklarert. Re-initialisering vil resultere i en kompileringstidsfeil. Dette gir pålitelig bruk av lenker, men reduserer fleksibiliteten i bruken. Vanligvis brukes referanser som funksjonsargumenter for å endre beståtte variabler i funksjoner. Følgende eksempel viser bruken av en slik funksjon:

Oppføring 4.4. Et eksempel på bruk av lenker.

void swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int agr1 = 10, arg2 = 5;
bytte(arg1, arg2);
returner 0;
}

I dette eksemplet tar swap()-funksjonen to argumenter, som er referanser til to variabler. Ved å bruke referansenavnene a og b, manipuleres variablene arg1 og arg2, definert i main()-funksjonen og sendt som parametere til swap()-funksjonen. Fordelen med swap()-funksjonen (som bruker referanser i stedet for pekere til variabler) er at den sikrer at funksjonen tar de riktige variabeltypene som argumenter, i stedet for annen informasjon, og at referansene initialiseres riktig før de brukes. Dette overvåkes av kompilatoren når den konverterer programtekst til objektkode og gir en feilmelding hvis bruken av referanser er feil. I motsetning til pekere, kan følgende operasjoner ikke utføres med referanser:

Du kan ikke få adressen til en lenke ved å bruke C++-adresseoperatøren;
du kan ikke tilordne en peker til en lenke;
du kan ikke sammenligne referanseverdier ved å bruke C++ sammenligningsoperatorer;
Du kan ikke utføre aritmetiske operasjoner på en referanse, for eksempel å legge til en offset;

void free(void *peker);

brukes til å frigjøre minnet pekerargumentet viser til. Først tildeles minne til applikasjonen, etter fullført arbeid med minne må det returneres, og gratisfunksjonen er ansvarlig for denne returen.

_msize

_msize-funksjonen returnerer størrelsen på minnet som er tildelt fra heapen:

size_t_msize(void*);

argument er en peker til en minneblokk. Funksjonen _msize returnerer størrelsen på minnet i byte. size_t er et heltall uten fortegn.

malloc

Malloc-funksjonen tildeler et minneområde fra haugen (dvs. et ledig minneområde):

void* malloc(størrelse_t);

argumentet spesifiserer antall byte som skal tildeles fra minnet. Malloc-funksjonen returnerer en void-peker til det tildelte minneområdet; den kan castes til ønsket type. Hvis det er mindre ledig minne for tildeling enn forespurt i size_t, vil malloc-funksjonen returnere NULL.

Et eksempel på arbeid med malloc-funksjonen:

/* Forfatter: @author Subbotin B.P..h> #include #inkludere int main(void) ( puts("Minne"); int *pointer; puts("for å få minne"); pointer = (int*)malloc(2 * sizeof(int)); int memorySize = _msize(pointer); printf("minnestørrelse = %dn", minnestørrelse); if(peker == NULL) (puts("Problemer"); returner EXIT_FAILURE; ) free(peker); setter("å frigjøre minne"); returner EXIT_SUCCESS; )

her er minneplass tildelt for en matrise som består av to elementer av typen int. Hvis minnetildelingen var vellykket, frigjør vi dette minneområdet ved å bruke gratisfunksjonen.

Vi får:

calloc

Calloc-funksjonen tildeler et minneområde og plasserer en matrise initialisert med nuller i den:

void* calloc(størrelse_t, størrelse_t);

det første argumentet er antall elementer, og det andre er størrelsen i byte til ett element. Produktet av argumentverdiene vil gi størrelsen på minneområdet som er forespurt for tildeling. Calloc-funksjonen returnerer en void-peker til det tildelte minneområdet; den kan castes til ønsket type. Hvis det er mindre ledig minne for tildeling enn forespurt, vil calloc-funksjonen returnere NULL.

Et eksempel på arbeid med calloc-funksjonen:

/* Forfatter: @author Subbotin B.P..h> #include #inkludere int main(void) ( puts("Minne"); int *pointer; puts("for å få minne"); pointer = (int*)calloc(2, sizeof(int)); int memorySize = _msize(pointer); printf("minnestørrelse = %dn", minnestørrelse); if(peker == NULL) (puts("Problemer"); returner EXIT_FAILURE; ) free(peker); setter("å frigjøre minne"); returner EXIT_SUCCESS; )

Eksemplet tildeler minne for en matrise av typen int som inneholder to elementer. Disse elementene initialiseres til null. Hvis minnetildelingen var vellykket, frigjør vi dette minneområdet ved å bruke gratisfunksjonen.

Vi får:

realloc

Realloc-funksjonen endrer størrelsen på et tidligere tildelt minneområde:

void* realloc(void*, størrelse_t);

det første argumentet er en peker til minneområdet hvis størrelse må endres, det andre argumentet spesifiserer den nye størrelsen på minneområdet. Hvis denne størrelsen er null og det første argumentet peker på et eksisterende minneområde, vil realloc returnere NULL og den opprinnelige minneblokken som det første argumentet peker på vil bli frigjort. Hvis det er mindre ledig minne for tildeling enn forespurt, vil realloc-funksjonen returnere NULL, og den opprinnelige minneblokken som er pekt på av det første argumentet vil bli bevart og forbli uendret. Realloc-funksjonen returnerer en void-peker til det tildelte minneområdet; den kan castes til ønsket type.

Dynamisk minnetildeling

Hovedapplikasjonsproblemer

Null-peker

En null-peker er en peker som lagrer en spesiell verdi som brukes for å indikere at en gitt pekervariabel ikke refererer til (peker på) noe objekt. I forskjellige programmeringsspråk er det representert av forskjellige konstanter.

·I C#- og Java-språk: null

·I C og C++ språk: 0 eller NULL makro. I tillegg introduserer C++11-standarden et nytt nøkkelord nullptr for å betegne en nullpeker

·I Pascal og Ruby: null

·På Component Pascal-språk:NIL

·I Python: Ingen

Pekere er vanskelige å håndtere. Det er ganske enkelt å skrive feil verdi til en peker, noe som kan forårsake en feil som er vanskelig å reprodusere. For eksempel endret du ved et uhell adressen til en peker i minnet, eller uriktig allokert minne for informasjon, og da kan en overraskelse vente deg: en annen veldig viktig variabel som bare brukes inne i programmet vil bli overskrevet. I dette tilfellet vil det være vanskelig for deg å reprodusere feilen. Det vil ikke være lett å forstå nøyaktig hvor feilen er. Og det vil ikke alltid være trivielt å eliminere det (noen ganger må du skrive om en betydelig del av programmet).

For å løse noen problemer er det metoder for beskyttelse og forsikring:

Etter å ha studert pekere på C-språket, oppdaget vi mulighetene for dynamisk minneallokering. Hva betyr det? Dette betyr at med dynamisk minneallokering blir minne reservert ikke på kompileringsstadiet, men på programutførelsesstadiet. Og dette gir oss muligheten til å allokere minne mer effektivt, hovedsakelig for arrays. Med dynamisk minneallokering trenger vi ikke stille inn størrelsen på matrisen på forhånd, spesielt siden det ikke alltid er kjent hvilken størrelse matrisen skal ha. La oss deretter se på hvordan minne kan tildeles.

Malloc()-funksjonen er definert i headerfilen stdlib.h, den brukes til å initialisere pekere med den nødvendige minnemengden. Minnet tildeles fra RAM-sektoren som er tilgjengelig for alle programmer som kjører på denne maskinen. Argumentet til malloc()-funksjonen er antall byte med minne som må tildeles; funksjonen returnerer en peker til den tildelte blokken i minnet. Malloc()-funksjonen fungerer akkurat som alle andre funksjoner, ikke noe nytt.

Siden forskjellige datatyper har forskjellige minnekrav, må vi på en eller annen måte lære hvordan vi får bytestørrelsen for forskjellige datatyper. For eksempel trenger vi en seksjon med minne for en matrise med verdier av typen int - dette er én størrelse minne, og hvis vi trenger å tildele minne for en matrise av samme størrelse, men av typen char - er dette en forskjellig størrelse. Derfor må du på en eller annen måte beregne minnestørrelsen. Dette kan gjøres ved å bruke sizeof()-operasjonen, som tar et uttrykk og returnerer størrelsen. For eksempel vil sizeof(int) returnere antall byte som trengs for å lagre en int-verdi. La oss se på et eksempel:


Yandex.Direct


#inkludere int *ptrVar = malloc(sizeof(int));

I dette eksemplet, i linje 3 ptrVar-pekeren tildeles en adresse til en minneplassering hvis størrelse tilsvarer int-datatypen. Dette minneområdet blir automatisk utilgjengelig for andre programmer. Dette betyr at etter at det tildelte minnet blir unødvendig, må det eksplisitt frigis. Hvis minnet ikke er eksplisitt frigitt, vil minnet ikke frigjøres for operativsystemet ved fullføring av programmet, dette kalles en minnelekkasje. Du kan også bestemme størrelsen på det tildelte minnet som må tildeles ved å sende en null-peker, her er et eksempel:



Som du kan se, er det et veldig sterkt poeng i denne notasjonen, vi bør ikke kalle malloc()-funksjonen ved å bruke sizeof(float). I stedet sendte vi en peker til float-typen til malloc(), i så fall vil størrelsen på det tildelte minnet automatisk bestemme seg selv!

Dette er spesielt nyttig hvis du trenger å allokere minne langt fra pekerdefinisjonen:


flyte *ptrVar; /* . . . hundre linjer med kode */ . . . ptrVar = malloc(sizeof(*ptrVar));

Hvis du skulle bruke en minneallokeringskonstruksjon med sizeof()-operasjonen, så må du finne definisjonen av en peker i koden, se på dens datatype, og først da vil du kunne allokere minne riktig.

Programmet ditt må gi nok minne til å lagre dataene det bruker. Noen av disse minnestedene tildeles automatisk. For eksempel kan vi erklære

char place = "Svineleverbukta";

og nok minne vil bli tildelt til å huske denne strengen.

Eller vi kan være mer spesifikke og be om en bestemt mengde minne:

int plate;

Denne beskrivelsen tildeler 100 minneplasseringer, som hver er utformet for å lagre en heltallsverdi.

C-språket stopper ikke der. Den lar deg tildele ekstra minne mens programmet kjører. Tenk deg for eksempel at du skriver et samtaleprogram og at du ikke på forhånd vet hvor mye data du må legge inn. Du kan tildele mengden minne du (som du tror) trenger, og deretter, om nødvendig, be om mer. I fig. 15.5 gir et eksempel som bruker funksjonen malloc()å gjøre nettopp det. Vær også oppmerksom på hvordan et slikt program bruker pekere.

/* legger til minne om nødvendig */

#inkludere

#define STOP " " /* signal for å stoppe inngang */

#define BLOKK 100 /* minnebyte */

#define LIM 40 /* grenselengde på inndatastreng */

#define MAKS 50 /* maksimalt antall inndatalinjer */

#define DRAMA 20000 /* lang tidsforsinkelse */

røye butikk; /* initial minneblokk */

karsymfe; /* inngangsstrengmottaker */

char *slutt; /* peker på slutten av minnet */

char *starter; /* peker på begynnelsen av linjer */

int indeks = 0; /* antall linjer som skal legges inn */

int teller; /* disk */

char *malloc(); /* minneallokator */

starter = lagre;

slutt = starter + BLOKK - 1;

puts("Nevn flere symfoniorkestre.");

puts("Skriv inn en om gangen: trykk [enter] i begynnelsen");

puts(" linjer for å fullføre listen. Ok, jeg er klar.");

while(strcmp(fgets(symph, LIM, stdin), STOP) != 0 && indeks< MAX)

( if(strlen(symfe) > slutt - starter)

( /* handlinger hvis det ikke er nok minne til å huske de angitte dataene */

puts(" Vent litt. Jeg skal prøve å finne litt ekstra minne.");

slutt = starter + BLOKK - 1;

for(tell = 0; tell< DRAMA; count++);

puts("Fant noen!"); )

strcpy(starter, symfe);

starter = starter + strlen(symfe) + 1;

if(++indeks< MAX)

printf("Dette er %d. Fortsett hvis du vil.n", index); )

puts("Ok, her er hva jeg har:");

for(tell = 0; tell< index; count ++)

setter(starter);

RIS. 15.5. Et program som legger til minne ved behov.

Her er et eksempel på programmet:

Nevn flere symfoniorkestre.

Skriv dem inn én om gangen; trykk [enter] i begynnelsen

linjer for å fullføre listen vår. Ok jeg er klar.

San Francisco Symphony.

Dette er 1. Fortsett hvis du vil.

Chicago Symphony

Dette er 2. Fortsett hvis du vil.

Berlin-filharmonien

Dette er 3. Fortsett hvis du vil.

Moskva-kammeret

Dette er 4. Fortsett hvis du vil. London Symphony

Dette er en 5. Fortsett hvis du vil. Wienerfilharmonien

Bare et sekund. Jeg skal prøve å finne ekstra minne.

Jeg fant noen!

Dette er 6. Fortsett hvis du vil.

Pittsburgh Symphony

Dette er 7. Fortsett hvis du vil.

Ok, her er hva jeg fikk:

San Francisco Symphony

Chicago Symphony

Berlin-filharmonien

Moskva-kammeret

London Symphony

Wienerfilharmonien

Pittsburgh Symphony

La oss først se hva funksjonen gjør malloc(). Det tar et usignert heltallsargument som representerer antall byte med minne som kreves. Så, malloc(BLOKK) krever 100 byte. Funksjonen returnerer en peker til typen røye til begynnelsen av en ny minneblokk. Vi brukte beskrivelsen

char *malloc();

for å advare kompilatoren om det malloc() returnerer en peker til typen røye. Derfor tildelte vi verdien til denne pekeren til array-elementet starter ved å bruke operatøren

starter = malloc(BLOKK);

Ok, la oss nå se på et programdesign som involverer lagring av alle de originale strengene på rad i et stort utvalg butikk. Vi ønsker å bruke starter for å referere til begynnelsen av den første linjen, starter[l]- den andre linjen osv. På mellomtrinnet legger programmet linjen inn i arrayen symf. Vi brukte fgets() i stedet for får() for å begrense inngangsstrengen til lengden på matrisen symf.

RIS. 15.6. Påfølgende symfelinjer skrevet til butikkmatrisen.

Før du kopierer symf V butikk, vi må sjekke om det er nok plass igjen til det. Peker slutt refererer til slutten av minnet og gjeldende verdi starter refererer til begynnelsen av ubrukt minne. Så vi kan sammenligne forskjellen mellom disse to pekerne med lengde symf og finne ut om det er nok minne igjen.

Hvis det ikke er nok plass ringer vi malloc() for å forberede ekstra minne. Vi installerer starter til begynnelsen av en ny minneblokk, en slutt- på slutten av en ny blokk. Merk at vi ikke har et navn på dette nye minnet. Det er for eksempel ikke en utvidelse butikk. Vi har kun betegnelser for pekere som peker til det nye minneområdet.

Når programmet kjører, blir hver ny rad referert til av et element i pekermatrisen starter. Noen linjer er inne butikk, andre i ett eller flere nye minneområder.

Men så lenge vi har pekere, kan vi jobbe med strenger, som utskriftsdelen av programmet viser oss.

Dermed brukes den malloc(). Men anta at du vil jobbe med hukommelse som int, men ikke røye. Du kan bruke den her også malloc(). Slik gjøres det:

char *malloc(); /* fortsatt beskrevet som en peker til char */

int *newmem;

newmem = (int *) malloc(l00); /* bruk typen støpeoperasjon */

Igjen kreves det 100 byte. Typecast-operasjonen konverterer verdien som returneres av en peker til en type røye, til en peker til en type int. Hvis, som i vårt system, int tar opp to byte med minne, noe som betyr at newmem + 1 vil øke pekeren med to byte, dvs. flytte den til neste heltall. Dette betyr også at 100 byte kan brukes til å lagre 50 heltall.

En annen mulighet for å tildele minne er gitt ved å bruke funksjonen calloc():

char *calloc();

lang *newmem;

newmem = (lang *) calloc(100, størrelse på(lang));

Som malloc() funksjon calloc() returnerer en peker til røye. Du må bruke type cast-operatoren hvis du vil huske en annen type. Denne nye funksjonen har to argumenter, som begge må være heltall uten fortegn. Det første argumentet inneholder antall minneplasseringer som kreves. Det andre argumentet er størrelsen på hver celle i byte. I vårt tilfelle lang

  • beskriv peker (skriv * peker; );
  • bestemme størrelsen på matrisen;
  • tilordne en del av minnet for å lagre matrisen og tilordne adressen til denne minneseksjonen til pekeren.

For å tildele minne i C++ kan du bruke den nye operatøren eller C-språkfunksjonene - calloc, malloc, realloc. Alle funksjoner er i stdlib.h-biblioteket.

5.2.1 malloc funksjon

Malloc-funksjonen tildeler en sammenhengende del av minnestørrelsesbyte og returnerer en peker til den første byten i den delen. Anropet til funksjonen ser slik ut:

void* malloc(størrelse_t størrelse);

der størrelse er en heltallsverdi uten fortegn 1 size_t er en grunnleggende heltallstype uten fortegn av C/C++-språket, som er valgt slik at det kan lagre den maksimale størrelsen på en teoretisk mulig matrise av enhver type. På et 32-bits operativsystem er size_t et usignert 32-bits tall (maksimal verdi 2 32 - 1), på et 64-bits operativsystem er det et 64-bits usignert tall (maksimal verdi 2 64 - 1)., som bestemmer størrelsen på det tildelte minnet i byte. Hvis minnereservasjonen var vellykket, returnerer funksjonen en variabel av typen void*, som kan konverteres til hvilken som helst ønsket pekertype. Hvis minne ikke kan tildeles, vil funksjonen returnere en tom peker NULL.

For eksempel,

dobbel *h; //Beskriv en peker for å doble. int k; cin>>k; //Skriv inn et heltall k. //Tildel et minneområde for å lagre k elementer av dobbel type. //Adressen til denne delen er lagret i variabelen h. h=(dobbel *) mallok (k* størrelse på (dobbel)); //h - adresse til begynnelsen av minneseksjonen, //h + 1, h + 2, h + 3, etc. - adresser til påfølgende elementer av dobbel type

5.2.2 calloc-funksjon

Calloc-funksjonen er designet for å allokere og tømme minne.

void * calloc(størrelse_t num, størrelse_t størrelse);

Ved å bruke funksjonen vil en minneseksjon bli tildelt hvor antall elementer av størrelsesbyte hver vil bli lagret. Alle elementer i det valgte området tilbakestilles til null. Funksjonen returnerer en peker til det tildelte området eller NULL hvis minne ikke kan tildeles.

For eksempel,

flyte *h; //Beskriv en peker som skal flyte. int k; cin>>k; //Skriv inn et heltall k. //Tildel et minneområde for å lagre k elementer av flytetype. //Adressen til denne delen er lagret i variabelen h. h=(float *) calloc (k, sizeof (float)); //h er adressen til begynnelsen av minneseksjonen, //h + 1, h + 2, h + 3, etc. er adressene til påfølgende elementer av flyttypen.

5.2.3 omfordelingsfunksjon

Realloc-funksjonen endrer størrelse på en tidligere tildelt minneplassering. Funksjonen er tilgjengelig slik:

void *realloc(void *p, størrelse_t størrelse);

hvor p er en peker til minneområdet hvis størrelse må endres til størrelse. Hvis adressen til et minneområde endres som følge av funksjonen, vil den nye adressen bli returnert som et resultat. Hvis den faktiske verdien av den første parameteren er NULL, fungerer realloc-funksjonen på samme måte som malloc-funksjonen, det vil si at den tildeler et minneområde med størrelsesbyte.

5.2.4 Gratis funksjon

Gratis-funksjonen brukes til å frigjøre tildelt minne. De henvender seg til henne slik:

void free(void *p);

hvor p er en peker til en minneplassering tidligere tildelt av malloc, calloc eller realloc.

5.2.5 Operatører nye og sletter

C++-språket har de nye operatørene å tildele og slette for å frigjøre et stykke minne.

For å allokere minne til å lagre n elementer av samme type, har den nye operatøren formen [