Programmeren in C/C-Preprocessor: verschil tussen versies

Verwijderde inhoud Toegevoegde inhoud
DimiC88 (overleg | bijdragen)
kGeen bewerkingssamenvatting
 
Regel 15:
Het is mogelijk, en niet eens zo vreselijk ongebruikelijk, niet alleen ''header-files'' ('''.h'''), maar ook andere formaten in te voegen, bijvoorbeeld specifieke stukken ''C'' broncode. Dit kan voordelen hebben, omdat op deze manier makkelijk verschillende stukken code voor verschillende versies van het uiteindelijke programma kunnen worden ingevoegd. Dit kan lelijk fout gaan als men per abuis de naam van het bestand zelf invoegt, dus in een bestand ''foobar.c'' een ''#include "foobar.c"'' neerzet. De consequenties hiervan zijn afhankelijk van de preprocessor in kwestie, maar leveren nooit het gewenste resultaat.
 
<sourcesyntaxhighlight lang="c">
/* een aantal standaard-bibliotheek includes. Onder unix staan
* deze in de regel in de directory ''/usr/include'', maar de
Regel 38:
#include "foo/foobar.h"
#include "bar/foobar.h"
</syntaxhighlight>
</source>
 
== #define ==
Een #define directive wordt gebruikt om een ''macro'' te definiëren. Een macro is een bepaalde tekst die door de C-Preprocessor wordt vervangen door de gewenste waarde. Deze macro's worden vaak gebruikt voor constante waarden, zodat deze slechts op een plaats hoeven te worden gewijzigd, in plaats van op verschillende plekken in de source code.
 
<sourcesyntaxhighlight lang="c">
/* definiëer FOOBAR */
#define FOOBAR 123
Regel 49:
/* en "ondefinieer" FOOBAR weer */
#undef FOOBAR
</syntaxhighlight>
</source>
 
Het verdient aanbeveling om in de code alle constanten met behulp van een macro te definiëren. Als ergens in de sourcecode ergens een getal '''10''' opduikt, is het niet duidelijk wat dat getal precies betekent. Door er met een macro een goed-gekozen naam aan te geven, is het onmiddellijk duidelijk welke functie die '''10''' heeft.
 
<sourcesyntaxhighlight lang="c">
#define NUM_USERS 10
#define RECORD_SiZE 10
#define MAX_LOG_ENTRIES_PER_DAY 10
</syntaxhighlight>
</source>
 
Als NUM_USERS verandert (de directie heeft eindelijk besloten de stokoude, overbelaste server te vervangen) hoeft de waarde '''10''' maar op een plaats vervangen te worden. Als de '''10''' ''hardcoded'' in de broncode staat, moeten alle bestanden apart worden nagegaan en voor iedere '''10''' moet bekeken worden of het om het aantal gebruikers gaat, de afmetingen van een record of het maximum aantal log-meldingen per dag. Onnodig te vermelden dat dat een hoofdpijn-klus is. Lijstjes als hierboven (en vaak nog veel langere) zijn dan ook een vast onderdeel van de broncode.
Regel 65:
Omdat sommige macro-definities vrij lang zijn, kunnen meerdere regels worden gebruikt. Dit wordt aangegeven door middel van een ''backslash'' ('''\''') direct voor het ''newline''-karakter. Dat wil zeggen dat na de ''backslash'' geen spaties of tabs meer mogen volgen. Technisch gesproken is dit "''escaping the newline''", het ontsnappen aan het nieuwe-regel-karakter. De backslash staat dan ook wel bekend als een ''escape''.
 
<sourcesyntaxhighlight lang="c">
/* macros met argumenten */
#define FOO(bar) (2*(bar)+1)
Regel 72:
#define HELLO(world) \
printf("Hello %s\n", world)
</syntaxhighlight>
</source>
 
Het is van belang te begrijpen dat het hier om tekstuele vervanging gaat. Als de boven gedefinieerde macro '''HELLO''' meermaals in de tekst wordt gebruikt, zal het ''printf''-statement evenzovele keren in de output-stream opduiken. Bij korte macro's is dit over het algemeen geen probleem,
Regel 81:
Tenslotte is het handig om er rekening mee te houden dat de macro-aanroep midden in een expressie kan staan. Omdat ''macro-expansie'' tekstueel plaatsvindt kan dit rare effecten hebben in verband met operator-precedentie, vooral als er sprake is van macro's met argumenten. Het is dan ook ten sterkste aan te raden niet zuinig te zijn met haakjes en de precedentie altijd expliciet te maken.
 
<sourcesyntaxhighlight lang="c">
/* macros met argumenten */
#define FOUT(oeps) 2*oeps +1
Regel 92:
int ook_goed = 5*GOED(1+1); /* 5 * ( 2 * (1+1) + 1) = 5 * (2 * 2 + 1) = 5 * 5 = 25 */
 
</syntaxhighlight>
</source>
 
== #if, #ifdef, #ifndef ==
Regel 100:
De '''#if''' preprocessor directive voegt afhankelijk van de expressie code in. Het is belangrijk daarbij op te merken dat de expressie alleen mag bestaan uit elementen die bekend zijn op het moment dat de C-Preprocessor actief is. Het is dus nutteloos (en zelfs fout) in de expressie C-variabelen of functie-aanroepen te gebruiken, die zijn namelijk pas bekend als de C-Compiler aan de beurt komt. De compiler zelf ziet uiteindelijk maar een van de ''printf''-statements.
 
<sourcesyntaxhighlight lang="c">
#if (FOOBAR == 2)
printf("FOOBAR is gedefinieerd en FOOBAR is gelijk aan 2\n");
Regel 106:
printf("FOOBAR is niet gedefinieerd of FOOBAR is niet gelijk aan 2\n");
#endif
</syntaxhighlight>
</source>
 
Het volgende voorbeeldje (ontleend aan ''real life'' code van een beunhaas) werkt niet naar behoren, wat de beunhaas in kwestie erg verbaasde. Het probleem zit er natuurlijk in dat ''sizeof(int) == 4'' geen constant_expressie is als de preprocessor aan het werk is. Deze heeft geen notie van ''int'' en kent geen ''sizeof'' operator.
 
<sourcesyntaxhighlight lang="c">
/* HEEL ERG FOUT! */
#if (sizeof(int) == 4)
Regel 117:
printf("sizeof(int) != 4\n");
#endif
</syntaxhighlight>
</source>
 
Deze constructie wordt ook af en toe gebruikt om bepaalde stukken code "uit te commenten", dat wil zeggen, uit te schakelen. Dat ziet er dan als volgt uit:
 
<sourcesyntaxhighlight lang="c">
void foobar(void)
{
Regel 134:
#endif
}
</syntaxhighlight>
</source>
 
=== #ifdef en #ifndef ===
Deze constructies werken op dezelfde manier als de #if-directive, met dien verstande dat de genoemde macro wel (#ifdef) of niet (#ifndef) gedefinieerd is.
 
<sourcesyntaxhighlight lang="c">
#ifdef FOOBAR
printf("FOOBAR is gedefinieerd\n");
Regel 145:
printf("FOOBAR is niet gedefinieerd\n");
#endif
</syntaxhighlight>
</source>
 
en
 
<sourcesyntaxhighlight lang="c">
#ifndef FOOBAR
printf("FOOBAR is niet gedefinieerd\n");
Regel 155:
printf("FOOBAR is gedefinieerd\n");
#endif
</syntaxhighlight>
</source>
 
De ''#ifndef'' constructie wordt veel gebruikt als ''guard'' (wachtpost) in header bestanden om te voorkomen dat interfaces dubbel worden gedefineerd als een header om de een of andere reden tweemaal wordt ingevoegd (met '''#include "foobar.h"''). Dit kan makkelijker optreden dan beginnelingen soms denken. Om deze reden is het gebruikelijk headerfiles met deze constructie te beschermen.
 
<sourcesyntaxhighlight lang="c">
/*
* FILE: foobar.h
Regel 183:
* End of file
*/
</syntaxhighlight>
</source>
 
== Voorgedefineerde macro's, __FILE__ en __LINE__ ==
Regel 200:
In de praktijk wordt deze functionaliteit vaak gebruikt om fouten op te sporen. In de source code ziet dat er dan ongeveer zo uit:
 
<sourcesyntaxhighlight lang="c">
#ifdef DEBUG_MODE
#define ERROR(text) fprintf(stderr, "file %s, line %d : %s", __FILE__, __LINE__, text)
Regel 206:
#define ERROR(text)
#endif
</syntaxhighlight>
</source>
 
iedere keer als '''ERROR''' wordt aangeroepen, worden de bestandsnaam en het regelnummer automatisch ingevoegd, wat foutmeldingen een stuk makkelijker traceerbaar maakt, maar als '''DEBUG_MODE''' niet is gedefineerd, wordt er geen enkele foutmelding gegenereerd. Constructies als deze (en er zijn nog veel fraaiere en handiger exemplaren) besparen veel tijd bij het zoeken naar fouten.
Informatie afkomstig van https://nl.wikibooks.org Wikibooks NL.
Wikibooks NL is onderdeel van de wikimediafoundation.