Hoe C-arrays werken: willekeurige getallen sorteren met code

14

U moet duizend gehele getallen opslaan. Wil je echt int a, b, c... helemaal tot z declareren en dan doorgaan? Nee. Dat is vervelend en gevoelig voor fouten. In plaats daarvan gebruikt u een array.

Een array is een verzameling waarden van hetzelfde type. Het verpakt ze in één enkel geheugenblok. In C declareer je het als volgt:

int a[5];

Dat is alles. Vijf gehele getallen. Klaar om te gaan.

Waarom C-arrays bij nul beginnen

Hier is de truc die beginners laat struikelen. C-arrays zijn nul-geïndexeerd.

Als je int a[5] declareert, heb je vijf slots. Maar ze zijn genummerd 0, 1, 2, 3 en 4. Er is geen a[5]. Als u toegang probeert te krijgen tot a[5], leest u geheugen dat niet tot uw array behoort. C zal je niet tegenhouden. Het zal je alleen maar rotzooi opleveren of crashen. Dit is een functie, geen bug. Het is snel. Het is ook gevaarlijk.

U krijgt toegang tot elementen met behulp van vierkante haakjes. a[0] is het eerste item. a[4] is de laatste.

Willekeurige getallen genereren in C

Laten we iets nuttigs bouwen. We zullen een programma maken dat 10 willekeurige getallen genereert en deze sorteert.

Eerst heb je de cijfers nodig. De standaardbibliotheek van C bevat rand(), maar laten we eens kijken naar een klassieke implementatie om de werking te begrijpen. Deze code maakt gebruik van een lineaire congruentiële generator, een methode uit het K&R C-boek.

Let op de regel #define MAX 10. Hierdoor ontstaat een constante. Constanten worden volgens afspraak in hoofdletters geschreven. Het zorgt ervoor dat ze opvallen. Je declareert de array int a[MAX] buiten de functie main. Dit maakt het een globale variabele. Het bestaat overal in het programma.

De variabele rand_seed is ook globaal. Het begint bij 10. Omdat het zaad vastligt, zijn de “willekeurige” getallen eigenlijk elke keer dat u het programma uitvoert hetzelfde. Als je echte willekeur wilt, zou je het met de systeemtijd zaaien. Voor nu is consistentie goed voor foutopsporing.

Bellensortering begrijpen

Nu komt het moeilijkste deel. Sorteren.

We gebruiken bellensortering. Het is het eenvoudigste sorteeralgoritme. Het is ook de langzaamste. Maar het leert je hoe lussen en arrays op elkaar inwerken.

Voeg deze code toe aan je main -functie, ter vervanging van de opmerking over “meer dingen”:

Wat gebeurt hier?

De buitenste lus loopt MAX-1 keer. De binnenste lus loopt minder vaak per doorgang. Waarom? Omdat de grootste getallen bij elke passage naar het einde van de array “bubbelen”. U hoeft ze niet opnieuw te controleren.

Binnen de binnenste lus vergelijken we a[y] met a[y+1]. Als de linker groter is, ruilen we ze. We gebruiken een tijdelijke variabele t om de waarde vast te houden terwijl we de stukken verplaatsen.

“De enige gemakkelijke manier om echt te begrijpen wat deze code doet, is door deze met de hand uit te voeren.”

Neem een ​​stuk papier. Teken vijf dozen. Zet er cijfers in. Voer de code regel voor regel uit. Verplaats de cijfers. Je zult de grote aantallen naar de bodem zien zinken. De kleintjes drijven omhoog. Het is visueel. Het is mechanisch.

Veelvoorkomende fouten in de C-array

C houdt uw hand niet vast. Je zult van de rand vallen.

  • Geen bereikcontrole. Als je toegang krijgt tot a[10] in een array van grootte 10, zal C niet schreeuwen. Het zal het volgende geheugen lezen. Dit leidt tot subtiele bugs die moeilijk te vinden zijn.
  • Functieaanroepen hebben haakjes nodig. Je moet x = rand(); schrijven. Als u x = rand; schrijft, wijst u het geheugenadres van de functie toe aan x. Niet het resultaat. Het compileert. Het breekt.

Probeer dit

Lees niet alleen. Code.

  • Wijzig de lus die de array vult in één enkele regel. Kun jij het?
  • Verplaats de logica voor het sorteren van bellen naar zijn eigen functie. Noem het void bubble_sort(). Verplaats de variabelen x, y en t binnen die functie. Ze worden lokaal. De array a is globaal, dus u hoeft deze niet door te geven.
  • Verander rand_seed naar andere waarden. Bekijk de uitvoerverandering.

Arrays zijn van fundamenteel belang. Het zijn de bouwstenen van datastructuren. Beheers ze, en de rest van C wordt duidelijker. Als u de nulindex negeert, bent u uren bezig met het debuggen van een crash die drie stappen geleden heeft plaatsgevonden.