Dynamische arrays
Arnout van Kempen over rommelen in een digitale wereld.
Enkele afleveringen geleden hebben we het al over pointers gehad. Intussen weten we ook iets van arrays, en hoe een string eigenlijk een array van characters is, inclusief het gedonder dat dat oplevert als we de gebruiker vragen een string in te voeren. De vorige keer hebben we daar nog structs aan toegevoegd.
Bij de pointers, en vooral de strings, zagen we dat C weinig problemen voor de programmeur oplost. Dat heeft voordelen, je kan een programma echt naar je hand zetten, maar het heeft ook nadelen. Wie in C troep maakt, loopt al snel echt in de ellende.
Dat is vrij letterlijk het geval bij dynamische geheugen allocatie. Op zichzelf is dat een buitengewoon handige techniek. Je kan hiermee arrays maken waarvan je tijdens het programmeren nog niet weet hoe groot ze zullen zijn.
Een array waarvan je de lengte op voorhand kent, kan je declareren simpelweg door in blokhaken er achter te zetten hoeveel elementen je nodig hebt:
int a[5];
geeft een array die loopt van a[0] tot a[4], waarbij ieder element van het type int is.
Maar wat als je de gebruiker wilt laten bepalen hoe groot de array moet zijn? Je kan in C een array declareren met variabele lengte:
int lengte = 0;
scanf(“%d”, &lengte);
int a[lengte];
zou dus gewoon moeten werken (ook al is dit wel erg lelijk en compact voor de gebruiker).
Overigens, een array kan ook nog eens meerdimensionaal zijn, dus
int a[5][6];
werkt ook prima als je een tabel van 5 x 6 integers wil hebben. Maar dat terzijde.
Er is namelijk nog het punt van de dynamische geheugenallocatie en wat daar mis kan gaan. In stdlib.h vinden we de functies malloc(), calloc(), realloc(), free(). Malloc, ofwel Memory Allocation, is een functie waarmee het programma aan het besturingssysteem vraagt een blok geheugen te reserveren en een pointer naar dat blok terug te geven. Calloc doet hetzelfde, maar zet het geheugenblok meteen op 0. Realloc gebruik je om een bestaand blok een nieuwe omvang te geven en met Free geef je het besturingssysteem aan dat een gereserveerd blok weer mag worden vrijgegeven.
Op GitHub heb ik een programmaatje gezet waar dit gedemonstreerd wordt.
int *getal;
int aantal;
declareert een pointer die als dynamische array zal gaan werken, en een int waarmee we de lengte van de array kunnen opgeven. Met
getal = malloc( aantal * sizeof(int));
wordt het benodigde geheugen gereserveerd. Aantal geeft hier het aantal data-elementen aan. Dit moet nog worden vermenigvuldigd met de lengte van een int, want malloc() wil weten hoeveel bytes gereserveerd moeten worden. Na deze opdracht kan getal als een normale array worden benaderd, dus met getal[x]. Dat gebeurt dan ook bij het inlezen van elementen, en later bij het optellen van die elementen:
for(i=0;i<aantal;i++
som += getal[i];
Som is eerder geïnitialiseerd als 0. En de opdracht som += getal[i] is een korte versie van som=som+getal[i]
Er zit bij dit alles een addertje onder het gras, dat bij veel andere programmeertalen niet zichtbaar wordt. Wat gebeurt er als de pointer naar het gereserveerde geheugen een andere waarde krijgt, of zelfs verdwijnt omdat deze buiten de scope van een functie valt? Stel bijvoorbeeld dat we in bovenstaand voorbeeld de, nogal onzinnige, opdracht hadden gegeven:
getal=&som;
Dat is perfecte C syntax en de compiler zal dit dus zonder veel morren verwerken. Maar vanaf deze opdracht verwijst getal niet meer naar het gereserveerde geheugen, maar naar som. Er is geen enkele manier meer waarop we kunnen weten waar het gereserveerde geheugen zich precies bevindt, en dus kunnen we er niets meer mee doen. Niet eens aan het besturingssysteem vertellen dat het niet langer gereserveerd moet blijven. Wat we daarmee hebben gecreëerd heet een memory leak. Als je die maar groot genoeg maakt, loopt je geheugen vol met onbruikbare troep en is er geen geheugen meer voor jouw programma, of elk ander programma, om mee te werken. De meeste hogere programmeertalen hebben daarom een proces dat garbage collection heet. C heeft een dergelijke voorziening niet. Je zal dat dus zelf moeten regelen, of nog beter, zorgen dat je geen garbage veroorzaakt. Daarvoor zijn twee dingen essentieel:
1.Ken aan een pointer naar een dynamisch gealloceerd geheugen nooit een andere waarde toe.
2.Geef gealloceerd geheugen terug aan het besturingssysteem zodra dat kan, met free().
Als je die regels altijd naar de letter opvolgt, heb je geen garbage collection nodig, omdat je geen garbage veroorzaakt.
Wie mee wil doen met #klooienmetcomputers kan dat doen via GitHub. Maak een account op www.github.com en zoek naar Abmvk/kmc. Het account Abmvk volgen kan ook. Lezers zijn vrij te gebruiken wat ze willen en om zelf zaken toe te voegen of aan te passen, vragen te stellen of commentaar te leveren.
Gerelateerd
Gewone variabelen
Arnout van Kempen over rommelen in een digitale wereld.
Bestanden in soorten en maten
Arnout van Kempen over rommelen in een digitale wereld.
Alles draait om data
Arnout van Kempen over rommelen in een digitale wereld.
Omgeving: input-output
Arnout van Kempen over rommelen in een digitale wereld.
Omgeving: de configuratie
Arnout van Kempen over rommelen in een digitale wereld.