9 Meer over datatypen We hebben al gezien dat het gebruik van symbolische constanten de leesbaarheid van een programma kan verbeteren. Door een geschikte naam (identifier) voor een constante te definiëren, zoals in #define AUGUSTUS 8 kunnen we de betekenis van de 'constante' AUGUSTUS zien in plaats van de specifieke waarde. Als het nodig blijkt de waarde van deze constante te veranderen, hoeven we alleen de definitie te veranderen in plaats van het hele programma te doorzoeken naar het voorkomen van de letterlijke constante 8. Om soortgelijke redenen levert C de mogelijkheid van een typedefinitie, waarin de programmeur een type een naam geeft. Deze mogelijkheid wordt in de volgende paragraaf bestudeerd. Tot nu toe hebben we de elementaire datatypen van C gezien. Soms hebben we een grotere keus nodig. We willen misschien een datatype gebruiken waarvan de waarden de dagen van de week zijn. Dan kunnen we op een natuurlijke manier de toekenning dag = MAANDAG; schrijven, waarin MAANDAG een constante is van dit datatype en dag een variabele van datzelfde type. In C kunnen we onze eigen datatypen specificeren. Deze faciliteiten verbeteren de mogelijkheid programma's te formuleren in de abstractie termen van het probleemgebied. Deze concepten bespreken we in paragraaf 9.2. 9.1 De statement typedef De programmeertaal C kent een aantal elementaire datatypen, zoals char en int. In een declaratie voor variabelen van de elementaire typen worden specificators gebruikt als char en int. De declaratie int uren, minuten; specificeert dat de variabelen uren en minuten van type int zijn. De statement typedef int Tijd; 1
specificeert dat de typenaam Tijd equivalent is met het datatype int van C. De opgegeven identifier kan later op de gebruikelijke manier worden gebruikt om een variabele of functie te declareren. De declaratie Tijd uren, minuten; declareert de variabelen uren en minuten als zijnde van het type Tijd, dat equivalent is met het type int. Het voornaamste voordeel van het gebruik van de typedefinitie in bovenstaand voorbeeld ligt in de verbeterde leesbaarheid van de definitie van variabelen. De declaratie laat ook zien waarvoor de variabelen in het programma bedoeld zijn. Als ze op de normale manier als int zouden worden gedeclareerd, zou de bedoeling van de variabelen niet zo duidelijk zijn. Een tweede voordeel is dat op deze manier afkortingen voor lange declaraties mogelijk zijn. Dat zullen we zien als we arrays en structuren gaan gebruiken. Om een nieuwe naam te definiëren, gaat u als volgt te werk. Schrijf een declaratie alsof u een variabele van het bedoelde type declareert. Om bijvoorbeeld een variabele var van type int te declareren, gebruikt u: int var; Vervang de naam van de variabele dan door de nieuwe typenaam: int Tijd; Plaats tenslotte het gereserveerde sleutelwoord typedef voor het geheel: typedef int Tijd; Het gebruik van de statement typedef wordt in programma 9-1 gedemonstreerd. Het programma krijgt als invoer een datum, gerepresenteerd door drie integers voor dag, maand en jaar. Het programma berekent het dagnummer van de datum in het betreffende jaar. In elk jaar heeft 1 januari dag nummer 1. In een schrikkeljaar heeft 3l december dag nummer 366, anders 365. Programma 9-1 Lees een geldige datum in de vorm DD/MM/JJJJ en bepaal het dagnummer binnen het jaar. In elk jaar heeft 1 januari dagnurnmer 1. Het dagnummer van 31 december is in een schrikkeljaar 366 en anders 365. 2
#include <stdio.h> #define IS_SCHRIKKEL(J) ((J % 4 == 0) && (J % 100!= 0) ½½ (J % 400 == 0)) typedef int Dag; // typenamen typedef int Maand; typedef int Jaar; typedef int Dagnummer; typedef int Dagenpermaand; typedef int Boolean; Dagenpermaand dagenpermaand (const Maand, const Jaar); int main() { Dag dag; // ingevoerde waarde Maand maand, m; // m is maandteller Jaar jaar; Dagnummer dagnummer; // berekende waarde scanf("%2d/%2d/%4d", &dag, &maand, &jaar); dagnummer = 0; for (m = l; m < maand; m++) dagnummer += dagenpermaand(m, jaar); dagnummer += dag; printf ("%2d/%2d/%4d heeft dagnummer %d \n", dag, maand, jaar, dagnummer); Dagenpermaand dagenpermaand (const Maand maand, const Jaar jaar) { switch (maand) { case 4: case 6: // april, juni,... case 9: case 11: //... september, november return (30); case 2: /* februari -- speciaal geval if (IS_SCHRIKKEL(jaar)) return (29); else return (28); default: return (31); 3
Een in een typedef-statement ingevoerde typenaam kan in botsing komen met de namen van andere variabelen en functies in het programma. Dit wordt overloading van namen genoemd. In bovenstaand voorbeeld hebben we Dag gebruikt als typenaam en dag als variabele van dat type. Omdat C verschil maakt tussen hoofdletters en kleine letters, worden deze namen door de C compiler natuurlijk als verschillend beschouwd. Maar we mogen ook dezelfde naam voor een type en een variabele of functie gebruiken. Het is dus toegestaan te declareren: typedef int dag; en dag vakantie; long int dag; De compiler lost de dubbelzinnigheid op op grond van de context. Het programma wordt er natuurlijk wel moeilijker leesbaar door en als er problemen zijn, wordt het misschien ook moeilijker de fouten te vinden. Het is beter eigen typenamen met een hoofdletter te laten beginnen, zoals we hierboven hebben gedaan, of namen te gebruiken die verder niet in het programma voorkomen. 9.2 Verder gebruik van de preprocessor - enumerated type #include <stdio.h> #include <stdlib.h> /* om exit te kennen */ #include <values.h> /* om MAXINT te kennen */ #include <conio.h> /* om kbhit te kennen */ #include <iostream.h> /* om de C++ functies cin en cout te kennen */ #define PI 3.1415926536 // Het is goede gewoonte zo'n constante in uppercase te typen: // verschil met een gewone variabele. #define max(i,j) i>j?i:j // Let op: géén blanko tussen max en (!!! #define schrikkeljaar(j) ((j%4==0)&&(j%100!=0) (j%400==0)) // Let op: géén blanko tussen schrikkeljaar en (j)!!! 4
#define CONDCOMP 1 #define PC // Met enum edag definiëren we een nieuw opsommingstype. enum edag {maandag, dinsdag, woensdag, donderdag, vrijdag, zaterdag, zondag; // maandag heeft de integerwaarde 0.... zondag heeft de integerwaarde 6. // Zonder typedef dag enum edag; moet een variabele gedefiniëerd worden als: // enum edag weekdag; // Met typedef definiëren we een 'synoniem' van dit type: typedef enum edag dag; // zodat volgend bevel geldig is: dag dag2; enum erichting {oost=0, noord=90, west=180, zuid=270; // de verschillende richtingen hebben een zinvolle waarde als implementatie. typedef enum erichting richting; int main() { double straal, omtrek; int a,b,m,jaar; dag weekdag, dag1; richting naarwaar; cout << "Geef de straal van de cirkel: " << endl; cin >> straal; omtrek=2.*pi*straal; // \ : een bevel over twee lijnen uitsmeren: cout << "De omtrek van een cirkel met straal " << straal \ << " is " << omtrek << endl; cout << "Geef twee getallen: "; cin >> a >> b; m=max(a,b); // zie #define max(i,j) i>j?i:j cout << "Het grootste cijfer van " << a << " en " << b << \ " is: " << m << endl; 5
// conditioneel compileren: #if CONDCOMP cout << "Geef een jaartal in:"; cin >> jaar; if (schrikkeljaar(jaar)) cout << jaar << " is een schrikkeljaar" << endl ; else cout << jaar << " is geen schrikkeljaar" << endl ; #else // Dit deel is optioneel: indien niet nodig: geen #else! cout << "Geen schrikkeljaardeel gecompileerd." << endl; // In dit programma zal dit stuk code niet gecompileerd worden!! #endif #if! defined(pc) // Als PC niet gedefinieerd is: cout << "Dit programma werkt op geen PC" << endl; #endif cout << "Speciale constanten: " << endl; // DATE en TIME zijn characterstrings. cout << "Compilatiedatum: " << DATE << \ " en tijd :" << TIME << endl; // FILE bevat de filenaam van de sourcecode. cout << endl << "Voorbeelden op constanten: " << endl; weekdag = donderdag; weekdag++; dag1 = zondag; dag1++; // Let op: dag1++ is NIET maandag: integerwaarde=7 en dus onbekend cout << weekdag << endl; cout << dag1 << endl; naarwaar = west; cout << naarwaar << endl; while (!kbhit()); exit((int)0); 6