Hallo en welkom. Nu we dynamische geheugentoewijzing begrijpen, moeten
we het implementeren, we moeten weten hoe we het in c-code moeten doen.
Dus we gaan het hebben over drie echt
belangrijke functies, malloc, calloc, realloc.
Je ziet dat ze een soort van namen hebben
waar geheugentoewijzing calloc en hertoewijzing.
De eenvoudigste en meest gebruikelijke manier om geheugen toe
te wijzen is via de standaardbibliotheekfunctie genaamd malloc.
Dit is het toewijzen van dynamisch geheugen tijdens runtime.
Om deze functie aan te roepen, moet u het standaard lib-headerbestand opnemen.
U geeft het aantal bytes geheugen op dat u als argument wilt toewijzen.
Wat door deze functie wordt geretourneerd, is het adres van de eerste geheugenbyte die is toegewezen.
Omdat u een adres terugkrijgt, is een aanwijzer de enige plaats om het te plaatsen.
Dit is de reden waarom dynamische geheugentoewijzing alleen mogelijk is via
dit concept van een aanwijzer omdat het adressen retourneert.
Je kunt alleen een adres in een pointer zetten, dat is wat een pointer opslaat, een pointer slaat een adres op.
Dus hier is hoe je het doet, heel eenvoudige syntaxis.
Wanneer u uw aanwijzer maakt, hoeft u dat meestal niet te doen.
U kunt uw aanwijzer maken en deze op null instellen.
En dan in de volgende regel, kun je malloc.
Maar hier is de standaardsyntaxis waarmee u uw int-aanwijzer maakt, aanwijzer
naar een type int.
En dan merk je dat je malloc belt.
En hier zeggen we malloc, we gaan 100 bytes maken voor malloc
en dan casten we het naar een int-pointer.
Omdat malloc alleen een adres retourneert, moet je het
naar een eindpointer casten omdat dat adres
een int-pointer opslaat.
Dit is de standaard manier om het te doen.
Nu vraagt u 100 bytes geheugen op
en wijst dit toe aan het adres van het geheugenblok tot p-nummer.
pNumber wijst naar de eerste int-locatie aan het begin van de 100 toegewezen bytes.
Dit betekent dus dat, aangezien gehele getallen 4 bytes per stuk zijn,
het 25 int-waarden kan bevatten.
En nogmaals, dit gaat ervan uit dat je 4 bytes hebt.
Nu, afhankelijk van het systeem dat u gebruikt, kunnen ze groter zijn, misschien kleiner.
Dus je zou het probleem hiermee moeten zien. Deze code gaat niet werken
op verschillende systemen omdat je toewijst op basis van een aanname, gebaseerd
op de aanname van de int-grootte.
Het is dus niet echt goed om malloc op
deze manier te gebruiken met een constant getal 100.
Wat u wilt doen, is dat u de operator sizeof wilt gebruiken.
Het zou dus beter zijn om de veronderstelling te verwijderen dat ints 4 bytes zijn.
Als u in plaats daarvan ruimte wilt toewijzen aan 25 gehele getallen binnen het malloc-argument, geeft u
het aantal gehele getallen op
dat u wilt toewijzen en vermenigvuldigt u dat vervolgens met de grootte van de gehele getallen.
En dit zal de code veel draagbaarder maken.
Deze code werkt nu op verschillende systemen, verschillende besturingssystemen, verschillende architecturen.
Je cast nog steeds de retourwaarde naar het adres omdat het naar een type int
moet wijzen, dus je zegt int star. Het enige verschil hier is dat u de operator sizeof gebruikt.
De operator Sizeof is erg belangrijk wanneer u geheugen toewijst met malloc of
met calloc of realloc.
U gebruikt en wilt altijd de operator sizeof gebruiken
voor het type dat u toewijst en dat vermenigvuldigt u vervolgens met
het aantal specifieke getallen dat u wilt, dus als u 25
int wilt opslaan, doet u 25.
Het bovenstaande argument van malloc geeft dus duidelijk aan dat er voldoende bytes beschikbaar moeten worden gesteld
voor het accommoderen van 25 waarden van het type int.
Let ook, zoals ik al zei, op de cast. Dit converteert het adres, geretourneerd
door de functie naar het type pointer naar int en int pointer.
Malloc retourneert een aanwijzer van het type void, dat is wat het zo handig maakt.
Het geeft dat adres terug, maar het is een lege aanwijzer.
Onthoud dat we het hadden over void-pointers en waarom void-pointers handig
zijn, omdat je nu een functie kunt hebben die verschillende parameters kan aannemen en
een void-pointer kan retourneren. Nu zie je de kracht van
het retourneren van een lege aanwijzer, maar
om het te gebruiken, om er daadwerkelijk gegevens aan toe te wijzen, om het te de-refereren, moet
je het casten.
Als we dit niet wilden casten, zouden we kunnen zeggen void star p-nummer.
We casten het niet, maar dan zouden we het uiteindelijk moeten
casten als we het willen de-refereren.
Maar daarom moeten we casten vanuit malloc. Het retourneert een adres en je wilt dat het verwijst
naar een int-type, en het retourneert een leeg type.
U kunt een willekeurig aantal bytes aanvragen.
Het is echter afhankelijk van uw systeem en hoeveel geheugen u op uw systeem heeft.
Dus als je probeert om een heleboel bytes te maken of aan te vragen of
als er een beperking is, kan het mislukken.
En als het mislukt, retourneert het de waarde null, malloc retourneert null.
En dit is goed omdat pointers wanneer pointers naar nul wijzen, dit betekent dat ze
niet naar een geheugen verwijzen, dus
u kunt niet per ongeluk enig geheugen beschadigen of wijzigen.
Het is dus altijd een goed idee om na het uitvoeren
van een malloc onmiddellijk te controleren op null voordat u het gebruikt,
net zoals het is wanneer u gegevens
toewijst of net zoals het is wanneer u de verwijzing naar een aanwijzer verwijdert.
Je wilt er zeker van zijn dat je echt naar iets verwijst.
Dus hier wijzen we wat geheugen toe voor p-nummer onze aanwijzer en
dan zeggen we zo niet p-nummer.
Onthoud dat de nul een nul is.
Je kunt er dus gewoon een not op gebruiken. Je hoeft het niet echt te vergelijken met null.
Dat kan, je kunt het vergelijken met null, het maakt het leesbaarder, misschien explicieter.
Maar je kunt gewoon geen p-nummer zeggen, en dat is eigenlijk niet één
zeggen, en nul is nul. Dit zal dus controleren op null.
En als het nul is, betekent dat in feite dat
het geheugen niet kon worden toegewezen. Als u geen geheugen in uw programma kunt toewijzen, heeft u een probleem.
Dus wat je normaal gesproken in deze code moet doen om een geheugentoewijzingsfout
op te lossen, is dat je moet afbreken, je moet
exit aanroepen, je moet uit je programma stappen. Want als je programma geen geheugen kan toewijzen, is je programma 'squeed'.
Dus meestal is dit dat je een ongeluk afbreekt als je geen geheugen kunt toewijzen.
Als u dit doet, kunt u tenminste een bericht weergeven om het
programma te beëindigen. Het is veel beter
dan het programma door te laten gaan als u dat niet was, we kunnen geen geheugen toewijzen.
Omdat je dan verder kunt gaan, zou je kunnen proberen om dat geheugen
te gebruiken, het zal nul zijn, dus het programma zal hoe dan ook crashen,
maar je zult niet per se weten waarom.
Je moet naar de call-stack kijken. Het zal veel langer duren om die fout te debuggen.
Geheugen vrijgeven. Nu zei ik dat, aangezien
jij de programmeur bent die verantwoordelijk is voor het toewijzen van het geheugen,
je het geheugen moet verwijderen als je het niet meer gebruikt, omdat geheugen een
essentiële hulpbron is, vooral op embedded systemen.
Je zou geen geheugen moeten gebruiken als je het niet nodig hebt. Dus je moet het geheugen vrijgeven.
U wilt het geheugen altijd vrijgeven als het niet meer nodig is.
Geheugen dat u op de heap toewijst, wordt automatisch vrijgegeven wanneer uw programma bestaat, wat goed
is omdat het wel wat opruimt.
Maar het is veel beter om het geheugen expliciet vrij te geven als je ermee klaar
bent, ook al is het net voordat je het afsluit.
Het is schoner,
doe het altijd gratis of laat het geheugen altijd vrij nadat je het
hebt toegewezen, je wilt deze regel volgen. Dit is een strikt strikte regel.
Maak altijd geheugen vrij als u het toewijst.
Een geheugenlek treedt op wanneer u wat geheugen dynamisch toewijst en u
de referentie niet behoudt, zodat u deze niet kunt vrijgeven.
Misschien valt u buiten het bereik van die referentie die u niet kunt. Je laat het niet los.
Dat heet een geheugenlek. Er is geheugen toegewezen, maar het wordt niet meer gebruikt.
Dit komt vaak voor wanneer u in een lus zit en u geheugen in een lus toewijst.
Je zit in de lus. U wijst dit geheugen toe. Je gebruikt het in de lus,
en dan spring je uit de lus en laat je hem niet meer los.
Elke iteratie van de lus wijst het toe.
Dus om geheugen vrij te maken dat u dynamisch hebt toegewezen,
moet u nog steeds toegang hebben tot dat adres.
Dus meestal wil je een vrij geheugen als je klaar bent met het gebruik ervan en
meestal in de methode die je het hebt toegewezen.
Als u dat geheugen echter doorgeeft aan een andere functie en dan is het
de verantwoordelijkheid van die functie, maar meestal wilt u het geheugen en de functie
die u eraan toewijst vrijgeven.
Om het geheugen vrij te geven voor een blok dynamisch toegewezen
geheugen waarvan u het adres in een aanwijzer hebt opgeslagen, hoeft u alleen maar
de vrije functie aan te roepen.
Dit staat ook in de standaard lib. Je geeft gewoon de aanwijzer door naar de gratis functie.
En meestal wil je, nadat je die aanwijzer hebt gepasseerd, deze op null zetten,
omdat dat betekent dat je er geen geheugen voor hebt toegewezen.
De gratis functie heeft een formele parameter van het type void
star, dus je kunt elk type aanwijzer doorgeven
en het zal werken.
Je hoeft het niet echt te casten of iets dergelijks.
Zolang p-nummer het adres bevat dat is geretourneerd toen het geheugen werd toegewezen, is
het hele geheugenblok vrij voor verder gebruik. Andere programma's kunnen dat geheugen gebruiken, jouw
programma kan dat geheugen nu gebruiken,
belangrijk als je op een embedded systeem werkt.
En dan, zoals ik al zei, wil je de aanwijzer altijd
op nul zetten om aan te geven dat die aanwijzer nergens meer naar verwijst.
Die herinnering is bevrijd.
Er is nog een leuke functie genaamd calloc.
Dit heeft een aantal voordelen ten opzichte van malloc. Het lijkt erg op malloc,
maar het wijst het geheugen toe als een aantal elementen in een bepaalde
grootte en initialiseert ook het geheugen.
Dat is het verschil, initialisatie. Ik had het over het belang van initialisatie.
Daarom wilt
u altijd gegevens initialiseren, zodat u begrijpt dat er
gegevens zijn die u verwacht.
Wanneer u gegevens de-initialiseert, kunnen er gegevens in staan waar u geen idee van heeft.
Initialiseer uw gegevens dus altijd ergens mee.
En calloc is een beetje beter omdat het het voor je doet.
Er zijn twee argumenten voor nodig, het heeft twee parameters.
Het aantal data-items waarvoor ruimte nodig is, de grootte van elk data-item.
Het wordt gedeclareerd in de standaard lib-header.
Je noemt het net zoals je malloc noemt. Ziet er ongeveer hetzelfde uit.
In dit voorbeeld maken we een
integer-pointer en wijzen we 75 integers van sizeof
int toe. Er zijn dus twee parameters nodig.
Het is een beetje schoner omdat je niet echt hoeft te vermenigvuldigen.
In malloc moest je 75 keer een evenement doen.
Hier zeg je gewoon 75, je
maakt 75 ints en de grootte voor elk is 4 bytes of wat
het systeem ook retourneert van de grootte van de operator.
Maar nogmaals, de betekenis hiervan is dat het ook al die gegevens gaat initialiseren, de geretourneerde waarde kan nul
zijn als het niet in staat is om dat geheugen toe te wijzen, wat op zijn beurt niet moet worden afgebroken.
Maar het lijkt erg op het gebruik van malloc, behalve dat het grote pluspunt deze initialisatie is.
Alle ints worden op nul geïnitialiseerd, en dat is echt een groot probleem,
een groot voordeel.
Het is ook een beetje gemakkelijker te gebruiken met de twee parameters.
Laten we het hebben over een andere functie genaamd realloc.
Met de realloc-functie kunt u geheugen dat u eerder met malloc of calloc
hebt toegewezen, opnieuw gebruiken of uitbreiden.
Dit is dus een manier om dynamisch te wijzigen hoeveel geheugen wordt toegewezen
terwijl u bezig bent.
Dus je zou in eerste instantie zoveel geheugen kunnen creëren en dan besef je
dat dat niet genoeg geheugen is. Wel, wat je daar kunt doen, is gewoon realloc
bellen, en dit zal geheugen opnieuw toewijzen.
Het verwacht twee argumentwaarden, een aanwijzer die een adres bevat dat
eerder werd omgezet door een aanroep van malloc.
U hebt dus een aanwijzer waaraan wat geheugen is toegewezen of calloc en vervolgens de grootte
en bytes van het nieuwe geheugen dat u eraan wilt toewijzen.
Dit zal dus de hoeveelheid geheugen toewijzen die u opgeeft met het tweede
argument, dat is het tweede argument. Het draagt de inhoud van het eerder toegewezen
geheugen over, waarnaar wordt verwezen door de aanwijzer, zodat het automatisch weet hoeveel geheugen
u eerder had toegewezen omdat u de aanwijzer doorgeeft.
En dan voegt het daar nog het nieuw toegewezen geheugen aan toe.
En het geeft een lege aanwijzer terug naar het nieuwe geheugen.
Dus je moet het herschikken wanneer je realloc belt, net als de andere.
Het retourneert null als je niet in staat was om opnieuw toe te
wijzen, wat waarschijnlijk weer een fatale fout is en je zou moeten afbreken.
Het belangrijkste kenmerk van deze bewerking is dat
realloc de inhoud van het oorspronkelijke geheugengebied behoudt, u breidt alleen dat geheugen uit.
Het kan gefragmenteerd zijn omdat je het misschien niet in volgorde hebt kunnen doen, maar
je hebt nog steeds al dat geheugen dat je aan nu-gegevens kunt toewijzen.
Laten we een voorbeeld bekijken.
In dit voorbeeld gaan we malloc gebruiken en dan gaan we het geheugen opnieuw toewijzen en dan
gaan we het vrijmaken.
Dus hier maken we een karakteraanwijzer.
We initialiseren het niet naar null, dat zouden we waarschijnlijk wel moeten doen.
Maar er is geen geheugen toegewezen voor waar het naar verwijst.
Er is geheugen toegewezen om de aanwijzer op
te slaan, wat slechts een adres is. Maar als u naar dat adres probeert te gaan, wordt uw programma host.
Dus wat je doet, is malloc bellen.
Hier zeggen we malloc 15.
Dit creëert 15 bytes voor een karakteraanwijzer.
We casten het, omdat het een lege aanwijzer retourneert. Dus str is nu
een aanwijzer waaraan geheugen is toegewezen voor waarnaar het verwijst.
We kunnen dan str copy gebruiken om wat gegevens door te geven aan waar het naar
verwijst, een string en we
printen de string in het adres.
Welnu, dan realiseren we ons dat wat we 15 bytes hebben toegewezen
niet groot genoeg is.
Dus dan noemen we realloc, en we geven
de aanwijzer door die str is en we willen nog 25 bytes opnieuw toewijzen en dan retourneert
dat een ongeldig punt omdat we het opnieuw moeten casten.
Nu hebben we Jason nog steeds in ons geheugen.
Dus nu kunnen we string aaneengeschakeld noemen, en we koppelen aan de string com,
dus nu is het Jason. com en dan printen we het uit.
En dan, last but not least, waarschijnlijk
het belangrijkste van deze dia, dus we hebben geen geheugenlek, we noemen gratis.
Bel altijd gratis als u malloc belt.
Laten we dus eens kijken naar dit voorbeeld in codeblokken.
Hier is ons codevoorbeeld in
onze editor voor codeblokken.
En het eerste dat je zou moeten
opvallen, is dat we het standaard lib-headerbestand opnemen, omdat daar onze
malloc-functies en onze realloc-functies zich bevinden, evenals onze gratis functie.
Ook omdat we enkele str-functies, aaneenschakeling en kopiëren gebruiken,
nemen we string op. H.
Nu, op regel acht, gaan we onze karakteraanwijzer maken.
Er is geen geheugen voor toegewezen.
We moeten dit initialiseren op null omdat
het nog nergens naar verwijst. Op lijn 11 bellen we malloc.
En we zouden waarschijnlijk 15 keer de grootte van
char moeten zeggen voor het geval de grootte
van bytes van char anders is en dan casten we
het in een
karakteraanwijzer omdat malloc een leegte retourneert. Wij kopiëren het dan.
Als we dit probeerden te kopiëren en we deden niets op regel 11,
zou het programma crashen omdat we geen geheugen hebben toegewezen voor de string waarnaar wordt verwezen.
We printen dan gewoon de string en het adres.
Nu noemen we realloc.
We moeten meer gegevens opslaan dan 15 bytes, dus
we gaan opnieuw toewijzen met 25 bytes
en we willen waarschijnlijk opnieuw sizeof char doen om consistent
te zijn.
We casten dit en dan voegen we het samen met onze
bestaande geheugenlocatie die Jason bevat. com, en we gaan Jason uitprinten. com.
En dan, last but not least, op regel 20 maken we dat geheugen vrij dat
echt belangrijk is.
Dus laten we doorgaan en dit uitvoeren. En wat je zult zien is
dat wanneer we het voor de tweede keer printen, we de aaneenschakeling hebben.
Dus onze
eerste string, we hebben geheugen toegewezen en het is een adres 000C2918.
Vervolgens hebben we een nieuw adres gemaakt door opnieuw toe te wijzen en de bestaande grootte van de aanwijzer die we
hebben doorgegeven als argument te nemen.
Dus 000C0CD8 heeft eigenlijk meer data toegewezen
gekregen, de bestaande data
in jason en nog 25 bytes.
En dus wat je merkt is dat de aaneenschakeling werkt.
Want toen we opnieuw toewijzen,
hebben we de inhoud Jason in dat geheugen geplaatst en hebben we het gewoon verlengd.
Dus veel voordelen aan het gebruik van deze malloc, deze calloc en gratis realloc calls.
Deze wil je gebruiken, je wilt ze begrijpen. Vrij eenvoudige functies om te gebruiken.
Dus laten we in onze laatste dia enkele richtlijnen bespreken.
U wilt voorkomen dat u veel kleine hoeveelheden geheugen toewijst, dat is niet efficiënt.
Wanneer u geheugen op de
heap toewijst, heeft het wel wat overhead.
En als je kleine geheugenblokken toewijst, zal het veel meer overhead hebben dan het
toewijzen van minder grotere blokken.
Dus wijs het geheugen toe dat je
nodig hebt, maar doe het niet om de twee statements met hertoewijzing, doe
grote brokken.
Houd het geheugen alleen vast zolang je het nodig hebt. Geheugen is een schaars goed.
Als het op de stapel ligt, is het nog schaarser.
Dus als je klaar bent met een blok geheugen, bel dan gratis.
Zorg er altijd voor dat u voorziet in het vrijgeven van geheugen dat u hebt toegewezen, beslis waar
in uw code u het geheugen vrijgeeft wanneer u de code schrijft die het toewijst.
Zorg ervoor dat u toegang hebt tot die aanwijzer.
Als u de aanwijzer globaal wilt maken, moet u dat doen.
Maar meestal, waar u ook in die functie toewijst, moet u het in die functie vrijgeven.
Als je andere functies hebt die geheugen voor
je vrijmaken, ga je ervan uit dat dat niet goed is. Er zijn daar te veel koppelingen en afhankelijkheden.
De persoon die het maakt, moet de persoon zijn die het verwijdert.
Leg de verantwoordelijkheid niet bij andere functies en dergelijke.
Zorg ervoor dat je niet per ongeluk het adres van het geheugen dat je op de heap
hebt toegewezen overschrijft voordat je het hebt vrijgegeven, dit zal een geheugenlek veroorzaken.
Dus als u te veel bytes probeert te schrijven of als u overflows heeft, kunt
u problemen ondervinden.
Wees dus vooral voorzichtig wanneer u geheugen binnen een lus
toewijst, omdat dit problemen kan veroorzaken bij bufferoverloop.
Ik hoop dat dit nuttig was. Dynamische geheugentoewijzing is een groot probleem.
Ik geef je een uitdaging die je helpt hiermee vertrouwd te raken,
maar zorg ervoor dat je dit concept begrijpt. Dank u.