Sådan forstå dit programs hukommelse

Når du koder på et sprog som C eller C ++, kan du interagere med din hukommelse på en mere lavt niveau måde. Nogle gange skaber dette mange problemer, du ikke fik før: segfaults . Disse fejl er ret irriterende og kan forårsage mange problemer. De er ofte indikatorer for, at du bruger hukommelse, du ikke skal bruge.

Et af de mest almindelige problemer er adgang til hukommelse, der allerede er frigivet. Dette er hukommelse, som du enten har frigivet med freeeller hukommelse, som dit program automatisk har frigivet, for eksempel fra stakken.

At forstå alt dette er virkelig enkel, og det vil helt sikkert få dig til at programmere bedre og på en smartere måde.

Hvordan er hukommelsen opdelt?

Hukommelse er opdelt i flere segmenter. To af de vigtigste for dette indlæg er stakken og bunken . Stakken er et ordnet indsættelsessted, mens bunken er tilfældig - du tildeler hukommelse, hvor du kan.

Stack-hukommelse har et sæt måder og operationer til sit arbejde. Det er her, nogle af din processors registeroplysninger gemmes. Og det er, hvor relevante oplysninger om dit program går - hvilke funktioner kaldes, hvilke variabler du oprettede og nogle flere oplysninger. Denne hukommelse administreres også af programmet og ikke af udvikleren.

Bunken bruges ofte til at allokere store mængder hukommelse, der formodes at eksistere, så længe udvikleren ønsker det. Når det er sagt, er det udviklerens job at kontrollere brugen af ​​hukommelsen på bunken . Når du bygger komplekse programmer, skal du ofte tildele store klumper hukommelse, og det er her, du bruger bunken. Vi kalder dette dynamisk hukommelse .

Du placerer tingene på bunken hver gang du bruger malloctil at allokere hukommelse til noget. Ethvert andet opkald, der int i;fungerer som stakhukommelse. At vide dette er virkelig vigtigt, så du nemt kan finde fejl i dit program og yderligere forbedre din Segfault-fejlsøgning.

Forståelse af stakken

Selvom du måske ikke ved det, tildeler dit program konstant stakhukommelse, så det kan fungere. Hver lokal variabel og enhver funktion, du kalder, går der. Med dette kan du gøre mange ting - de fleste af dem er ting, som du ikke ønskede at ske - som bufferoverløb og adgang til forkert hukommelse.

Så hvordan fungerer det virkelig?

Stakken er en LIFO (Last-In-First-Out) datastruktur. Du kan se det som en kasse med perfekt monterede bøger - den sidste bog, du placerer, er den første, du tager ud. Ved at bruge denne struktur kan programmet nemt styre alle dets operationer og rækkevidde ved hjælp af to enkle operationer: push og pop .

Disse to gør nøjagtigt det modsatte af hinanden. Push indsætter værdien til toppen af ​​stakken. Pop tager topværdien af ​​det.

For at holde styr på det aktuelle hukommelsessted er der et specielt processorregister kaldet Stack Pointer . Hver gang du har brug for at gemme noget - som en variabel eller returadressen fra en funktion - skubber den og flytter stakmarkøren op. Hver gang du forlader en funktion, popper den alt fra stakmarkøren, indtil den gemte returadresse fra funktionen. Det er simpelt!

For at teste om du forstod, lad os bruge følgende eksempel (prøv at finde fejlen alene ☺️):

Hvis du kører det, vil programmet simpelthen segfault. Hvorfor sker dette? Alt ser på plads! Undtagen omkring ... stakken.

Når vi kalder funktionen createArray, stakken:

  • gemmer returadressen
  • skaber arri stakhukommelse og returnerer den (en matrix er simpelthen en markør til en hukommelsesplacering med dens information)
  • men da vi ikke brugte mallocdet bliver gemt i stack hukommelse.

Når vi har returneret markøren, da vi ikke har nogen kontrol over stakoperationer, popper programmet informationen fra stakken og bruger den efter behov. Når vi forsøger at udfylde arrayet, efter at vi kom tilbage fra funktionen, ødelægger vi hukommelsen - hvilket gør programmet til fejl.

Forstå bunken

I modsætning til stakken er bunken det, du bruger, når du vil have noget at eksistere i nogen tid uafhængigt af funktioner og rækkevidde. For at bruge denne hukommelse er C-sproget stdlib virkelig godt, da det bringer to fantastiske funktioner: mallocog free.

Malloc (hukommelsesallokering) anmoder systemet om den mængde hukommelse, der blev bedt om, og returnerer en markør til startadressen. Gratis fortæller systemet, at den hukommelse, vi bad om, ikke længere er nødvendig og kan bruges til andre opgaver. Ser virkelig simpelt ud - så længe du undgår fejl.

Systemet kan ikke tilsidesætte, hvad udviklere bad om. Så det afhænger af os mennesker at styre det med de to funktioner ovenfor. Dette åbner døren for en menneskelig fejl: Memory Leaks.

Memory Leak er hukommelse, der blev anmodet om af brugeren, der aldrig blev frigivet - da programmet sluttede, eller henvisninger til dets placeringer gik tabt. Dette gør, at programmet bruger meget mere hukommelse, end hvad det skulle. For at undgå dette frigør vi det, hver gang vi ikke længere har et bunketildelt element.

På billedet ovenfor frigør den dårlige måde aldrig den hukommelse, vi brugte. Dette ender med at spilde 20 * 4 byte (int-størrelse i 64-bit) = 80 byte. Dette ser måske ikke så meget ud, men forestil dig ikke at gøre dette i et kæmpe program. Vi kan ende med at spilde gigabyte!

Det er vigtigt at administrere din bunkehukommelse for at gøre dine programmer hukommelse effektive. Men du skal også være forsigtig med, hvordan du bruger den. Ligesom i stack-hukommelsen, efter at hukommelsen er frigjort, kan du få adgang til den eller bruge den til en segfault.

Bonus: Structs og bunken

En af de almindelige fejl ved brug af strukturer er bare at frigøre strukturen. Dette er fint, så længe vi ikke tildelte hukommelse til markører inde i strukturen. Hvis hukommelse er allokeret til markører inde i strukturen, skal vi først frigøre dem. Så kan vi frigøre hele strukturen.

Hvordan jeg løser mine hukommelseslækageproblemer

Det meste af tiden, når jeg programmerer i C, bruger jeg strukturer. Derfor har jeg altid to obligatoriske funktioner, der skal bruges sammen med mine strukturer: konstruktøren og destruktoren .

Disse to funktioner er de eneste, hvor jeg bruger mallocs og frigør på strukturen. Dette gør det virkelig simpelt og let at løse mine hukommelseslækager.

(Hvis du gerne vil vide mere om at gøre koden lettere at læse, skal du tjekke mit indlæg om abstraktion).

Et godt værktøj til hukommelsesadministration - Valgrind

Det er svært at administrere din hukommelse og sørge for, at du håndterede alt korrekt. Et godt værktøj til at validere, hvis dit program opfører sig korrekt, er Valgrind. Dette værktøj validerer dit program og fortæller dig, hvor meget hukommelse du tildelte, hvor meget der blev frigivet, hvis du forsøgte at skrive i et forkert hukommelsesområde ... Brug af det er en fantastisk måde at validere, hvis alt er i orden, og man bør bruge det til at undgå sikkerhedskompromisser.

Glem ikke at følge mig!

Udover at poste her på Medium er jeg også på Twitter.

Hvis du har spørgsmål eller forslag, så tøv ikke med at kontakte mig.