Stap voor stap uitwerking van een eenvoudige C opdracht.

© Harry Broeders.

Deze pagina is bestemd voor studenten van de Haagse Hogeschool - Academie voor Technology, Innovation & Society Delft.

Opdracht.

Bij het vak GESPRG krijg je regelmatig een opdracht die als volgt begint: Schrijf een programma dat ... Hoe pak je zoiets (als beginnend programmeur) nu aan? Als voorbeeld neem ik de opdracht: schrijf een programma dat een geheel getal 0 < n < 7 inleest en vervolgens de tafels van 1 t/m n naast elkaar afdrukt.

Stap voor stap.

De methode die ik gebruik om tot een werkend programma te komen dat aan de opdracht voldoet is stapgewijze verfijning (Engels: stepwise refinement). Ik begin met een eenvoudig programma en breid het programma stap voor stap uit tot ik uiteindelijk het gewenste programma heb. Ik test het programma na elke stap. De bovenstaande opdracht zou ik als volgt aanpakken.

Stap 0. Bezint eer gij begint.

De eerste stap heeft nog niets met programmeren te maken. Voordat ik begin te programmeren denk ik eerst over de opdracht na. Snap ik wat ik moet maken? Het helpt vaak als ik mogelijke testgevallen bedenk. Bijvoorbeeld: bij de invoer 4 moet het programma de volgende uitvoer produceren:

 1 x 1 =  1  1 x 2 =  2  1 x 3 =  3  1 x 4 =  4
 2 x 1 =  2  2 x 2 =  4  2 x 3 =  6  2 x 4 =  8
 3 x 1 =  3  3 x 2 =  6  3 x 3 =  9  3 x 4 = 12
 4 x 1 =  4  4 x 2 =  8  4 x 3 = 12  4 x 4 = 16
 5 x 1 =  5  5 x 2 = 10  5 x 3 = 15  5 x 4 = 20
 6 x 1 =  6  6 x 2 = 12  6 x 3 = 18  6 x 4 = 24
 7 x 1 =  7  7 x 2 = 14  7 x 3 = 21  7 x 4 = 28
 8 x 1 =  8  8 x 2 = 16  8 x 3 = 24  8 x 4 = 32
 9 x 1 =  9  9 x 2 = 18  9 x 3 = 27  9 x 4 = 36
10 x 1 = 10 10 x 2 = 20 10 x 3 = 30 10 x 4 = 40

Bij het zien van deze gewenste uitvoer realiseer ik mezelf dat het erg belangrijk is dat de regels van de tafels netjes onder elkaar worden afgedrukt. Ik herinner me hoe in de les is behandeld dat je bij het afdrukken van een geheel getal met behulp van printf een veldbreedte kunt opgeven, bijvoorvbeeld printf("3d", getal); zal de waarde van de variabele getal op een veld van 3 karakters breed afdrukken. Als getal de waarde 7 heeft dan wordt dus afgedrukt <spatie><spatie>7.

Stap 1. Alle begin is moeilijk makkelijk.

Elk C programma heeft hetzelfde begin en einde. Ik heb een bestandje c.c gemaakt waarin een C programma staat dat niets doet. Ik begin met een kopietje van dat bestand en noem dat tafels_stap1.c:

#include <stdio.h>

/* © 2012 Harry Broeders */

int main(void) {

    /* Hier komt de code */

    fflush(stdin);
    getchar();
    return 0;
}

Stap 2. Invoer.

Het is altijd handig om met de invoer te beginnen. Ik begin dus met het eerste deel van de opdracht: schrijf een programma dat een geheel getal 0 < n < 7 inleest. Ik herinner mij uit de les dat ik een geheel getal kan inlezen met behulp van scanf. Voordat ik de waarde van n inlees wil ik de gebruiker eerst vertellen wat er van hem/haar verwacht wordt door op het scherm te zetten: Geef de waarde van n (1..6): , dat kan ik doen met behulp van printf. Om te kijken of het inlezen is gelukt druk ik de waarde van n af. Zie tafels_stap2.c:

#include <stdio.h>

/* © 2012 Harry Broeders */
/* Dit programma leest een geheel getal 0 < n < 7 
   en drukt vervolgens de tafels van 1 t/m n naast elkaar af */

int main(void) {
    int n;

    printf("Geef de waarde van n (1..6): ");
    scanf("%d", &n);

    printf("Test n = %d", n);

    fflush(stdin);
    getchar();
    return 0;
}

Als ik de waarde 4 invoer dan ziet de uitvoer van het programma er als volgt uit (de invoer is onderstreept weergegeven):

Geef de waarde van n (1..6): 4
Test n = 4

Als ik de waarde -3 invoer dan ziet de uitvoer van het programma er als volgt uit:

Geef de waarde van n (1..6): -3
Test n = -3

Stap 3. Controle op invoer.

Ik bedenk me dat het netjes is om te controlleren of het ingevoerde getal binnen de gestelde grenzen ligt. Wat moet er gebeuren als de gebruiker een getal invoert dat buiten deze grenzen ligt? Ik besluit om de gebruiker opnieuw te vragen om de waarde in te voeren (zonder foutmelding) en de ingevoerde waarde opnieuw in te lezen. Ik wil dus het programmadeel:

    printf("Geef de waarde van n (1..6): ");
    scanf("%d", &n);

herhalen totdat een geldige waarde is ingevoerd. Ik herinner me uit de les dat er verschillende herhalingsinstructies zijn (for, do while en while). Het bovenstaande programmadeel moet minstens 1x herhaald worden en het is afhankelijk van de gebruiker hoe vaak het herhaald moet worden. Het programmadeel moet dus een onbekend aantal maal >=1 herhaald worden. Om die reden moet ik (volgens de theorieles) kiezen voor een do while lus. Zie tafels_stap3.c:

#include <stdio.h>

/* © 2012 Harry Broeders */
/* Dit programma leest een geheel getal 0 < n < 7 
   en drukt vervolgens de tafels van 1 t/m n naast elkaar af */

int main(void) {
    int n;

    do {
        printf("Geef de waarde van n (1..6): ");
        scanf("%d", &n);
    } while(n < 1 || n > 6);

    printf("Test n = %d", n);

    fflush(stdin);
    getchar();
    return 0;
}

Bij het testen probeer ik eerst een aantal ongeldige waarden en constateer dat het programma correct werkt:

Geef de waarde van n (1..6): -3
Geef de waarde van n (1..6): 0
Geef de waarde van n (1..6): 8000
Geef de waarde van n (1..6): 7
Geef de waarde van n (1..6): 4
Test n = 4

Stap 4. Eerste regel van de tafels afdrukken.

De waarde van n wordt nu dus succesvol ingelezen en op de juiste wijze gecontroleerd. Het tweede deel van de opdracht was: ... en vervolgens de tafels van 1 t/m n naast elkaar afdrukt. Het tweede deel van de opdracht is voor een beginnend programmeur misschien te moeilijk om in één stap uit te voeren. Ik bedenk dat het niet zo moeilijk is om de eerste regel van de tafels af te drukken. Dus bij de invoer 4 moet het programma de volgende uitvoer produceren:

 1 x 1 =  1  1 x 2 =  2  1 x 3 =  3  1 x 4 =  4

en bij de invoer 6 moet het programma de volgende uitvoer produceren:

 1 x 1 =  1  1 x 2 =  2  1 x 3 =  3  1 x 4 =  4  1 x 5 =  5  1 x 6 =  6

Het valt meteen op dat het stukje uitvoer  1 x i =  i wordt herhaald voor i = 1 tot en met i = n. Hierbij is i het nummer van de tafel. We moeten het programmadeel dat dit stukje print dus n maal herhalen. Het aantal herhalingen is dus bekend als de herhaling begint (want de waarde van n is dan al ingelezen). Omdat het aantal herhalingen bekend is moet ik (volgens de theorieles) kiezen voor een for lus. Zie tafels_stap4.c:

#include <stdio.h>

/* © 2012 Harry Broeders */
/* Dit programma leest een geheel getal 0 < n < 7 
   en drukt vervolgens de tafels van 1 t/m n naast elkaar af */

int main(void) {
    int n, tafel;

    do {
        printf("Geef de waarde van n (1..6): ");
        scanf("%d", &n);
    } while(n < 1 || n > 6);

    for (tafel = 1; tafel < n + 1; tafel = tafel + 1) {
        printf(" 1 x %d = %2d ", tafel, 1 * tafel);
    }
    printf("\n");

    fflush(stdin);
    getchar();
    return 0;
}

Bij het testen blijkt dat het programma correct werkt:

Geef de waarde van n (1..6): 6
 1 x 1 =  1  1 x 2 =  2  1 x 3 =  3  1 x 4 =  4  1 x 5 =  5  1 x 6 =  6

Stap 5. Alle 10 regel van de tafels afdrukken.

Als ik alle 10 regels van de tafels wil afdrukken dan moet ik het programmadeel waarin de eerste regel wordt afgedrukt 10x herhalen. Het programmagedeelte dat herhaald moet worden is dus:

    for (tafel = 1; tafel < n + 1; tafel = tafel + 1) {
        printf(" 1 x %d = %2d ", tafel, 1 * tafel);
    }
    printf("\n");

De constante 1 die in de eerste regel wordt afgedrukt moet in de tweede regel vervangen worden door de constante 2, in de derde regel door de constante 3 enz. Ik maak een variabele factor aan die "loopt" van 1 t/m 10. Omdat het aantal herhalingen bekend is (10x) gebruik ik een for loop. Zie tafels_stap5.c:

#include <stdio.h>

/* © 2012 Harry Broeders */
/* Dit programma leest een geheel getal 0 < n < 7 
   en drukt vervolgens de tafels van 1 t/m n naast elkaar af */

int main(void) {
    int n, tafel, factor;

    do {
        printf("Geef de waarde van n (1..6): ");
        scanf("%d", &n);
    } while(n < 1 || n > 6);

    for (factor = 1; factor < 11; factor = factor + 1) {
        for (tafel = 1; tafel < n + 1; tafel = tafel + 1) {
            printf("%2d x %d = %2d ", factor, tafel, factor * tafel);
        }
        printf("\n");
    }

    fflush(stdin);
    getchar();
    return 0;
}

Bij het testen blijkt dat het programma correct werkt:

Geef de waarde van n (1..6): 6
 1 x 1 =  1  1 x 2 =  2  1 x 3 =  3  1 x 4 =  4  1 x 5 =  5  1 x 6 =  6
 2 x 1 =  2  2 x 2 =  4  2 x 3 =  6  2 x 4 =  8  2 x 5 = 10  2 x 6 = 12
 3 x 1 =  3  3 x 2 =  6  3 x 3 =  9  3 x 4 = 12  3 x 5 = 15  3 x 6 = 18
 4 x 1 =  4  4 x 2 =  8  4 x 3 = 12  4 x 4 = 16  4 x 5 = 20  4 x 6 = 24
 5 x 1 =  5  5 x 2 = 10  5 x 3 = 15  5 x 4 = 20  5 x 5 = 25  5 x 6 = 30
 6 x 1 =  6  6 x 2 = 12  6 x 3 = 18  6 x 4 = 24  6 x 5 = 30  6 x 6 = 36
 7 x 1 =  7  7 x 2 = 14  7 x 3 = 21  7 x 4 = 28  7 x 5 = 35  7 x 6 = 42
 8 x 1 =  8  8 x 2 = 16  8 x 3 = 24  8 x 4 = 32  8 x 5 = 40  8 x 6 = 48
 9 x 1 =  9  9 x 2 = 18  9 x 3 = 27  9 x 4 = 36  9 x 5 = 45  9 x 6 = 54
10 x 1 = 10 10 x 2 = 20 10 x 3 = 30 10 x 4 = 40 10 x 5 = 50 10 x 6 = 60

Stap 6. Een laatste verbetering.

Bij het testen type ik per ongeluk een letter in. Het programma komt nu in een oneinige loop. Hier is een deel van de uitvoer:

Geef de waarde van n (1..6): a
Geef de waarde van n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (
1..6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): Geef de waarde
van n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): Geef de
waarde van n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): G
eef de waarde van n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (1
..6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): Geef de waarde v
an n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): Geef de w
aarde van n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): Ge
ef de waarde van n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (1.
.6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): Geef de waarde va
n n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): Geef de wa
arde van n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): Gee
f de waarde van n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (1..
6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): Geef de waarde van
 n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): Geef de waa
rde van n (1..6): Geef de waarde van n (1..6): Geef de waarde van n (1..6): Geef

Bij navraag blijkt dit een bekend probleem te zijn. Zie: Hoe voorkom ik dat mijn programma rare dingen doet als in plaats van een getal een letter wordt ingetypt?

Na bovenstaande verwijzing bestudeerd te hebben kom ik tot het volgende programma, zie tafels_final.c :

#include <stdio.h>

/* © 2012 Harry Broeders */
/* Dit programma leest een geheel getal 0 < n < 7 
   en drukt vervolgens de tafels van 1 t/m n naast elkaar af */

int main(void) {
    int factor, tafel, n;

    do {
        printf("Geef de waarde van n (1..6): ");
        fflush(stdin);
    } while (scanf("%d", &n) != 1 || n < 1 || n > 6);

    for (factor = 1; factor < 11; factor = factor + 1) {
        for (tafel = 1; tafel < n + 1; tafel = tafel + 1) {
            printf("%2d x %d = %2d ", factor, tafel, factor * tafel);
        }
        printf("\n");
    }

    fflush(stdin);
    getchar();
    return 0;
}

Bij het testen blijkt dat het programma correct werkt ook als je tekst invoert:

Geef de waarde van n (1..6): a
Geef de waarde van n (1..6): Doe maar wat!
Geef de waarde van n (1..6): -3
Geef de waarde van n (1..6): 0
Geef de waarde van n (1..6): 7
Geef de waarde van n (1..6): 300
Geef de waarde van n (1..6): 3
 1 x 1 =  1  1 x 2 =  2  1 x 3 =  3
 2 x 1 =  2  2 x 2 =  4  2 x 3 =  6
 3 x 1 =  3  3 x 2 =  6  3 x 3 =  9
 4 x 1 =  4  4 x 2 =  8  4 x 3 = 12
 5 x 1 =  5  5 x 2 = 10  5 x 3 = 15
 6 x 1 =  6  6 x 2 = 12  6 x 3 = 18
 7 x 1 =  7  7 x 2 = 14  7 x 3 = 21
 8 x 1 =  8  8 x 2 = 16  8 x 3 = 24
 9 x 1 =  9  9 x 2 = 18  9 x 3 = 27
10 x 1 = 10 10 x 2 = 20 10 x 3 = 30