|
Defines en Macros
Defines
Bestudeer het programma DEFINE.C. Zoals je wellicht ziet, beginnen de
eerste regels met het woord #define. Dit is de manier waarop
in C define's en macro's worden gedefinieerd. Voordat wezenlijk met
compileren wordt begonnen, gaat het programma door de pre-processor,
waarin een zoek en vervang proces plaats vindt. Overal waar
in het voorbeeld programma de tekst 'START' wordt gevonden, zal de
pre-processor deze tekst vervangen door '0' (nul), omdat dit als zo
danig is gedefinieerd. De C compiler zelf zal de tekst 'START' nooit
tegenkomen en denken dat er altijd een nul heeft gestaan.
#define START 0 /* Start waarde voor iteratie */
#define WAARDE 5 /* Waarde voor vergelijking */
#define STOP 9 /* Stop waarde voor iteratie */
#define MAX(a,b) ((a)>(b)?(a):(b)) /* MAX functie als macro */
#define MIN(a,b) ((a)>(b)?(b):(a)) /* MIN functie als macro */
main ()
{
int index, min, max;
for (index = START; index <= STOP; index++) {
max = MAX(index, WAARDE);
min = MIN(index, WAARDE);
printf ("Het maximum is %d en het minimum is %d\n", max, min);
}
}
Overal waar met een startwaarde moet worden gewerkt, kan in het
programma het woord START worden geprogrammeerd in plaats van de
numerieke nul (0). Op deze manier wordt het programma beter leesbaar en
vooral beter onderhoudbaar.
In het kleine voorbeeld programma maakt het niet veel uit op welke
manier je het doet. Als je echter een (zeer) omvangrijk programma hebt
met vele referenties naar het woord START, wordt het een andere zaak.
Moet de start waarde 1 (een) worden, dan behoeft slechts de ene
#define regel te worden aangepast om dit doel te bereiken.
Was met numerieke nullen gewerkt, dan was het behoorlijk wat moeilijker
geweest. Het wijzigen moet dan met de hand gebeuren, want niet alle
nullen in het programma hoeven start waarden te zijn. Vergeet je er een
dan werkt het programma niet meer correct.
Op dezelfde manier zal de pre-processor alle keren het woord STOP
vervangen door de numerieke 9 en WAARDE door de numerieke 5. De
compiler zal uiteindelijk geen weet meer van hebben wat er stond en het
voorbewerkte programma vertalen.
Het inmiddels gebruikelijk geworden bij het programmeren in C om voor
symbolische constanten (zoals START en STOP) alleen hoofdletters te
gebruiken.
In deze cursus hanteren we de Hungarian Notation. Voor
meer informatie daarover wordt verwezen naar :hdref refid=hungar..
Laten we eens een ander voorbeeld gaan bekijken. Straks als we In- en
Uitvoer gaan bespreken, zullen we een indicator nodig hebben die de
End-of-File situatie aangeeft bij Invoer (EOF).
Omdat de diverse compilers hiervoor verschillende numerieke waarden
gebruiken, zullen we programma's met behulp van een #define
schrijven, met een waarde voor EOF die bij de compiler hoort. Als we
dan later met een andere C compiler compileren, hoeven we slechts de
ene #define regel te veranderen en alles werkt.
Wanneer we de #define regels door de pre-processor het
programma binnen laten halen vanuit een bibliotheek die bij de compiler
werd geleverd, hoeven we helemaal niets te wijzigen en werkt het
programma meteen.
Macro's
Een macro is eigenlijk ook een #define. Het verschil zit
in het feit dat in een macro logische beslissingen en berekeningen
uitgevoerd kunnen worden.
Kijk nog eens naar het voorbeeld programma DEFINE.C, en dan met name
naar regel 4 en 5. Telkens wanneer de pre-processor het woord MAX
vindt, verwacht de pre-processor twee termen tussen haakjes erachter.
Het zoek en vervang mechanisme zal de meegegeven termen in
het tweede deel van de definitie vervangen. De eerste term zal elke 'a'
en de tweede term zal elke 'b' in het tweede deel van de definitie
vervangen.
Op regel 12 en 13 zal index worden ingevuld voor elke 'a'
en 5 voor elke 'b'. De tekst WAARDE was vooraf al gewijzigd in 5.
De programmatekst MAX (index, WAARDE); wordt dus
uiteindelijk door de C compiler gelezen als
((index)>(5)?(index):(5));. Het lijkt erop of er te veel
haakjes zijn overgebleven. Je vraagt je af of
(index>5?index:5); niet voldoende is. Welnu voor de
pre-processor en de compiler maakt het niet uit dat een en ander er
ingewikkeld uitziet, voor de resulterende statement echter wel. Dit
probleem bespreken we hierna. Compileer en test DEFINE.C nu eerst.
Bestudeer programma MACRO.C om nog wat beter kennis te maken met het
fenomeen macro. De zesde regel toont een foute macro met de naam
FOUTIEF. Niet in alle gevallen zal deze macro de inhoud van een kubus
correct berekenen. De macro INHOUD doet dit wel in alle gevallen
correct.
#define START 1 /* Start waarde voor iteratie */
#define OFFSET 5 /* Offset */
#define STOP 9 /* Stop waarde voor iteratie */
#define KWADRAAT(a) (a)*(a) /* Kwadraat berekenen */
#define INHOUD(a) (a)*(a)*(a) /* Macro voor berekenen inhoud */
#define FOUTIEF(a) a*a*a /* Foutieve inhouds macro */
main ()
{
int index;
for (index = START; index <= STOP; index++) {
printf ("Het kwadraat van %d is %d\n",
index + OFFSET, KWADRAAT (index + OFFSET));
printf ("De inhoud van %d is %d\n",
index + OFFSET, INHOUD (index + OFFSET));
printf ("De foutieve inhoud van %d is %d\n",
index + OFFSET, FOUTIEF (index + OFFSET));
}
}
Laten we het programma eens volgen voor de waarde index = 1.
De kubus heeft dan zijden 1 + 5 = 6, hetgeen resulteert in een inhoud
van 6 * 6 * 6 = 216. Bij gebruik van de macro INHOUD zullen de termen
inderdaad gevormd worden als (1+5)*(1+5)*(1+5) = 6*6*6 = 216. Met
behulp van FOUTIEF wordt echter 1+5*1+5*1+5 = 1+5+5+5 gevormd en dat is
16 hetgeen fout is.
Haakjes zijn dus echt noodzakelijk om een macro in alle gevallen
correct te laten functioneren. De macro FOUTIEF werkt goed als de termen
uit een enkel getal bestaan.
Het programma drukt een aantal keren kwadraat en inhoud van een waarde
af met behulp van printf(). Dit is reeds behandeld en zal verder
duidelijk zijn. Compileer en test dit programma.
|