C Programmering

malloc på c språk

malloc på c språk
Du kan komme hit av to grunner: enten du vil dynamisk tildele innhold, eller du vil vite mer om hvordan malloc fungerer. I begge tilfeller er du på rett sted! Dynamisk tildeling er en prosess som skjer mye, men generelt bruker vi den ikke selv: de aller fleste programmeringsspråk administrerer minne for deg, da det er en vanskelig jobb, og hvis du ikke klarer å gjøre det riktig, er det sikkerhetsmessige implikasjoner.

Men hvis du gjør C, C ++ eller monteringskode, eller hvis du implementerer en ny ekstern modul i ditt favorittprogrammeringsspråk, må du administrere din dynamiske minnetildeling selv.

Hva er dynamisk tildeling? Hvorfor jeg trenger malloc?

Vel, i alle applikasjoner, når du oppretter en ny variabel - det kalles ofte å erklære en variabel - du trenger minne for å lagre det. Ettersom datamaskinen din er i moderne dager, kan den kjøre mer enn ett program om gangen, og så bør hvert program fortelle det til operativsystemet ditt (her Linux) at den trenger den mengden minne. Når du skriver denne typen kode:

#inkludere
#inkludere
#define DISK_SPACE_ARRAY_LENGTH 7
ugyldig getFreeDiskSpace (int statsList [], size_t listLength)
komme tilbake;

int main ()
/ * Inneholder ledig diskplass de siste 7 dagene. * /
int freeDiskSpace [DISK_SPACE_ARRAY_LENGTH] = 0;
getFreeDiskSpace (freeDiskSpace, DISK_SPACE_ARRAY_LENGTH);
returner EXIT_SUCCESS;

FreeDiskSpace-arrayet trenger minne, så du må be Linux om godkjenning for å få litt minne. Imidlertid, da det er åpenbart når du leser kildekoden at du trenger en matrise på 7 int, ber kompilatoren automatisk Linux om det, og den tildeler den på bunken. Dette betyr i utgangspunktet at denne lagringen blir ødelagt når du returnerer funksjonen der variabelen blir deklarert. Derfor kan du ikke gjøre det:

#inkludere
#inkludere
#define DISK_SPACE_ARRAY_LENGTH 7
int * getFreeDiskSpace ()
int statsList [DISK_SPACE_ARRAY_LENGTH] = 0;
/ * HVORFOR GJØR VI DET??! statsList vil bli ødelagt! * /
return statsList;

int main ()
/ * Inneholder ledig diskplass de siste 7 dagene. * /
int * freeDiskSpace = NULL;
freeDiskSpace = getFreeDiskSpace ();
returner EXIT_SUCCESS;

Du ser lettere problemet nå? Deretter vil du sammenkoble to strenger. I Python og JavaScript vil du gjøre:

newStr = str1 + str2

Men som du vet, i C fungerer det ikke slik. Så for å bygge en URL, for eksempel, må du sammenkoble to strenger, for eksempel URL-bane og domenenavn. I C har vi strcat, ikke sant, men det fungerer bare hvis du har en matrise med nok plass til det.

Du vil bli fristet til å vite lengden på den nye strengen ved å bruke strlen, og du vil ha rett. Men hvordan vil du be Linux om å reservere denne ukjente mengden minne? Kompilatoren kan ikke hjelpe deg: den nøyaktige plassen du vil tildele er bare kjent under kjøretid. Det er akkurat der du trenger dynamisk tildeling og malloc.

Skriver min første C-funksjon ved hjelp av malloc

Før du skriver kode, en liten forklaring: malloc lar deg tildele et bestemt antall byte til applikasjonsbruken din. Det er veldig enkelt å bruke: du ringer malloc med antall byte du trenger, og det returnerer en peker til det nye området som Linux har reservert for deg.

Du har bare 3 ansvarsområder:

  1. Sjekk om malloc returnerer NULL. Det skjer når Linux ikke har nok minne til å gi.
  2. Frigjør variablene dine når de ikke er brukt. Ellers vil du kaste bort minne, og det vil redusere applikasjonen.
  3. Bruk aldri minnesonen etter at du har frigjort variabelen.

Hvis du følger alle disse reglene, vil alt gå bra, og dynamisk tildeling vil løse mange problemer. Fordi du velger når du skal frigjøre minnet, kan du også trygt returnere en variabel som er tildelt malloc. Bare ikke glem å frigjøre den!

Hvis du lurer på hvordan du frigjør en variabel, er det med gratisfunksjonen. Kall det med samme peker enn malloc returnerte deg, og minnet blir frigjort.

La meg vise deg med concat-eksemplet:

#inkludere
#inkludere
#inkludere
/ *
* Når du ringer til denne funksjonen, ikke glem å sjekke om returverdien er NULL
* Hvis det ikke er NULL, må du ringe gratis på den returnerte pekeren en gang verdien
* brukes ikke lenger.
* /
char * getUrl (const char * const baseUrl, const char * const toolPath)
size_t finalUrlLen = 0;
char * finalUrl = NULL;
/ * Sikkerhetskontroll. * /
hvis (baseUrl == NULL || toolPath == NULL)
retur NULL;

finalUrlLen = strlen (baseUrl) + strlen (toolPath);
/ * Ikke glem '\ 0', derav + 1. * /
finalUrl = malloc (sizeof (char) * (finalUrlLen + 1));
/ * Etter malloc-regler ... * /
hvis (finalUrl == NULL)
retur NULL;

strcpy (finalUrl, baseUrl);
strcat (finalUrl, toolPath);
retur finalUrl;

int main ()
char * googleImages = NULL;
googleImages = getUrl ("https: // www.Google.com "," / imghp ");
hvis (googleImages == NULL)
returner EXIT_FAILURE;

putter ("Verktøy-URL:");
setter (googleImages);
/ * Det trengs ikke lenger, frigjør det. * /
gratis (googleImages);
googleImages = NULL;
returner EXIT_SUCCESS;

Så du ser et praktisk eksempel for bruk av dynamiske tildelinger. Først unngår jeg fallgruver som å gi getUrl returverdi rett for å sette funksjon. Så tar jeg meg også tid til å kommentere og dokumentere at returverdien skal frigjøres ordentlig. Jeg ser også etter NULL-verdier overalt, slik at alt uventet kan fanges trygt i stedet for å krasje applikasjonen.

Til slutt tar jeg ekstra vare på å frigjøre variabelen og deretter sette pekeren til NULL. Det unngår å bli fristet til å bruke - selv ved en feil - den nå frigjorte minnesonen. Men som du ser, er det enkelt å frigjøre en variabel.

Du kan legge merke til at jeg brukte størrelse på malloc. Det gjør det mulig å vite hvor mange byte en røye bruker og tydeliggjør intensjonen i koden slik at den er mer lesbar. For røye er sizeof (røye) alltid lik 1, men hvis du bruker en rekke int i stedet, fungerer den nøyaktig på samme måte. For eksempel, hvis du trenger å reservere 45 int, gjør du bare:

fileSizeList = malloc (sizeof (int) * 45);

På denne måten ser du raskt hvor mye du vil tildele, derfor anbefaler jeg alltid bruken.

Hvordan fungerer malloc under panseret?

malloc og gratis er faktisk funksjoner som er inkludert i alle C-programmer som vil snakke med Linux på dine vegne. Det vil også gjøre dynamisk tildeling lettere fordi Linux ikke lar deg allokere variabler i alle størrelser i starten.

Linux gir to måter å få mer minne faktisk: sbrk og mmap. Begge har begrensninger, og en av dem er: du kan tildele bare relativt store mengder, for eksempel 4096 byte eller 8192 byte. Du kan ikke be om 50 byte som jeg gjorde i eksemplet, men du kan heller ikke be om 5894 byte.

Dette har en forklaring: Linux må holde en tabell der den forteller hvilket program som har reservert hvilken minnesone. Og denne tabellen bruker også plass, så hvis hver byte trengte en ny rad i denne tabellen, ville det være behov for en stor andel minne. Derfor er hukommelsen delt i store blokker med for eksempel 4096 byte, og omtrent som om du ikke kan kjøpe to og en halv appelsin i en dagligvarebutikk, kan du ikke be om halve blokker.

Så malloc tar disse store blokkene og gir deg et lite stykke av disse minneblokkene når du kaller det. I tillegg, hvis du frigjorde få variabler, men ikke nok til å rettferdiggjøre frigjøring av en hel blokk, kan malloc-systemet beholde blokker og resirkulere minnesoner når du ringer til malloc igjen. Dette har fordelen å gjøre malloc raskere, men minne reservert av malloc kan ikke brukes i noen annen applikasjon, mens programmet for øyeblikket ikke bruker det i virkeligheten.

Men malloc er smart: hvis du ringer malloc for å tildele 16 MiB eller et stort beløp, vil malloc sannsynligvis be Linux om fullblokker dedikert bare for denne store variabelen ved å bruke mmap. Når du ringer gratis, vil det mer sannsynlig unngå sløsing med plass. Ikke bekymre deg, malloc gjør en bedre jobb med resirkulering enn mennesker gjør med søppelet vårt!

Konklusjon

Jeg tror nå du bedre forstår hvordan alt dette fungerer. Selvfølgelig er dynamisk tildeling et stort tema, og jeg tror vi kan skrive en full bok om emnet, men denne artikkelen bør gjøre deg komfortabel med konseptet både generelt og med praktiske programmeringsråd.

Hvordan installere og spille Doom på Linux
Introduksjon til Doom Doom-serien stammer fra 90-tallet etter utgivelsen av den originale Doom. Det ble en øyeblikkelig suksess, og fra den tiden og u...
Vulkan for Linux-brukere
Med hver nye generasjon grafikkort ser vi spillutviklere skyve grensene for grafisk troskap og komme et skritt nærmere fotorealisme. Men til tross for...
OpenTTD vs Simutrans
Å lage din egen transportsimulering kan være morsom, avslappende og ekstremt fristende. Derfor må du sørge for at du prøver så mange spill som mulig f...