Voorbeeld van een zinvolle macro met argumenten.

© Harry Broeders.

Deze pagina is bestemd voor studenten van de THRijswijk groepen EPm en EPv die de module SOPX1 volgen.

In les 12 heb ik verteld dat het gebruik van #define met argumenten beter vervangen kan worden door een functie. Op deze regel is (zoals op elke regel) natuurlijk wel een uitzondering te bedenken.

Het aantal elementen in een array berekenen.

Met behulp van de operator sizeof kun je het aantal bytes van een variabele (of van een type) bepalen.

Het onderstaande programma sizeof.c bepaalt het aantal bytes van het type char, het type int, een variabele van het type double en een int array van 10 elementen.

#include <stdio.h>

int main() {
    double d;
    int rij[10];
    printf("Een variabele van het type char is %d bytes groot.\n", sizeof(char));
    printf("Een variabele van het type int is %d bytes groot.\n", sizeof(int));
    printf("De variabele d van het type double is %d bytes groot.\n", sizeof d);
    printf("De variabele rij een array van 10 int's is %d bytes groot.\n", sizeof rij);
    getchar();
    return 0;
}

Op een 32 bits machine geeft dit programma het volgende resultaat:

Een variabele van het type char is 1 bytes groot.
Een variabele van het type int is 4 bytes groot.
De variabele d van het type double is 8 bytes groot.
De variabele rij een array van 10 int's is 40 bytes groot.

Met behulp van de slimme formule sizeof rij/sizeof rij[0] kun je het aantal elementen van een array berekenen. Het maakt niet uit van welk type de elementen in de array zijn! Snap je de formule?

In het onderstaande programma sizeof_rij.c wordt dit gedemonstreerd.

#include <stdio.h>

int main() {
    int rij1[10];
    double rij2[8];
    printf("De variabele rij1 bevat %d elementen.\n", sizeof rij1/sizeof rij1[0]);
    printf("De variabele rij2 bevat %d elementen.\n", sizeof rij2/sizeof rij2[0]);
    getchar();
    return 0;
}

De uitvoer van dit programma is als volgt:

De variabele rij1 bevat 10 elementen.
De variabele rij2 bevat 8 elementen.

Het lijkt een goed idee om de formule in een functie op te nemen (dat is beter onderhoudbaar en herbruikbaar).

Het onderstaande programma sizeof_rij_functie.c geeft echter niet het verwachte resultaat:

#include <stdio.h>
/* PAS OP! Dit programma geeft reslutaten die je misschien niet verwacht */

int aantal_int(int rij[]) {
    return sizeof rij/sizeof rij[0];
}

int aantal_double(double rij[]) {
    return sizeof rij/sizeof rij[0];
}

int main() {
    int rij1[10];
    double rij2[8];
    printf("De variabele rij1 bevat %d elementen.\n", aantal_int(rij1));
    printf("De variabele rij2 bevat %d elementen.\n", aantal_double(rij2));
    getchar();
    return 0;
}

Het eerste wat opvalt is dat je voor elk type array een aparte functie moet schrijven. Begrijp je waarom? Een groter probleem is echter dat de uitvoer anders is dan je zou verwachten. Op een 32 bits machine is de uitvoer als volgt:

De variabele rij1 bevat 1 elementen.
De variabele rij2 bevat 0 elementen.

Hoe is deze uitvoer te verklaren? Bij het aanroepen van een functie wordt alleen het beginadres van de array doorgegeven. De variabele rij die we in de functie gebruiken is dus het beginadres van de array. Op een 32 bits machine is een adres 32 bits = 4 bytes groot. De expressie sizeof rij levert in de functies dus altijd de waarde 4 op. In de functie aantal_int is rij[0] een int en geeft sizeof rij[0] dus de waarde 4 (op een 32 bits machine). Het resultaat van de functie aantal_int is dus altijd 4/4 = 1. In de functie aantal_double is rij[0] een double en geeft sizeof rij[0] dus de waarde 8. Het resultaat van de functie aantal_double is dus altijd 4/8 = 0.

Bij het gebruik van een #define met een parameter is het resultaat wel zoals verwacht en is het niet nodig om voor elk type een aparte macro te schrijven. Dit wordt in het programma sizeof_rij_macro.c gedemonstreerd:

#include <stdio.h>

#define aantal_elementen(rij) (sizeof (rij)/sizeof (rij)[0])

int main() {
    int rij1[10];
    double rij2[8];
    printf("De variabele rij1 bevat %d elementen.\n", aantal_elementen(rij1));
    printf("De variabele rij2 bevat %d elementen.\n", aantal_elementen(rij2));
    getchar();
    return 0;
}

De uitvoer van dit programma is zoals verwacht:

De variabele rij1 bevat 10 elementen.
De variabele rij2 bevat 8 elementen.

De macro aantal_elementen kun je nu bijvoorbeeld gebruiken in het programma om de inhoud van een array om te keren dat je als extra opgave hebt gemaakt (keerom2.c).

#include <stdio.h>

void keerom(int rij[], int aantal) {
     int index;
     for (index=0; index<aantal/2; ++index) {
         int hulpje=rij[index];
         rij[index]=rij[aantal-index-1];
         rij[aantal-index-1]=hulpje;
     }
}

void drukaf(int rij[], int aantal) {
     int index;
     for (index=0; index<aantal; ++index) {
         printf("%d ", rij[index]);
     }
     printf("\n");
}

#define aantal_elementen(rij) (sizeof (rij)/sizeof (rij)[0])

int main() {
    void keerom(int rij[], int aantal);
    void drukaf(int rij[], int aantal);
    int testrij1[]={12, 11, 17, 23};
    int testrij2[]={12, 11, 13, 17, 23};
    printf("Voor keerom:\n");
    drukaf(testrij1, aantal_elementen(testrij1));
    keerom(testrij1, aantal_elementen(testrij1));
    printf("Na keerom:\n");
    drukaf(testrij1, aantal_elementen(testrij1));
    printf("Voor keerom:\n");
    drukaf(testrij2, aantal_elementen(testrij2));
    keerom(testrij2, aantal_elementen(testrij2));
    printf("Na keerom:\n");
    drukaf(testrij2, aantal_elementen(testrij2));
    getchar();
    return 0;
}

Opgave.

Een programmeur die slim denkt te zijn heeft het bovenstaande programma als volgt herschreven (keerom3.c):

#include <stdio.h>
/* PAS OP! Dit programma geeft reslutaten die je misschien niet verwacht */

#define aantal_elementen(rij) (sizeof (rij)/sizeof (rij)[0])

void keerom(int rij[]) {
     int index;
     int aantal=aantal_elementen(rij);
     for (index=0; index<aantal/2; ++index) {
         int hulpje=rij[index];
         rij[index]=rij[aantal-index-1];
         rij[aantal-index-1]=hulpje;
     }
}

void drukaf(int rij[]) {
     int index;
     for (index=0; index<aantal_elementen(rij); ++index) {
         printf("%d ", rij[index]);
     }
     printf("\n");
}

int main() {
    void keerom(int rij[]);
    void drukaf(int rij[]);
    int testrij1[]={12, 11, 17, 23};
    int testrij2[]={12, 11, 13, 17, 23};
    printf("Voor keerom:\n");
    drukaf(testrij1);
    keerom(testrij1);
    printf("Na keerom:\n");
    drukaf(testrij1);
    printf("Voor keerom:\n");
    drukaf(testrij2);
    keerom(testrij2);
    printf("Na keerom:\n");
    drukaf(testrij2);
    getchar();
    return 0;
}

De uitvoer van dit programma is anders dan de programmeur verwacht:

Voor keerom:
12
Na keerom:
12
Voor keerom:
12
Na keerom:
12

Kun jij deze slimmerik uitleggen waarom zijn programma niet werkt zoals hij verwacht?