|
Standaard Invoer/Uitvoer
De STDIO.H header file
Bestudeer het programma INVUITV.C voor een voorbeeld van standaard
Invoer/Uitvoer. Standaard Invoer/Uitvoer refereert aan die gevallen
waarbij gegevens worden gelezen van het toetsenbord of geschreven naar
het beeldscherm. Omdat dit zo vaak voorkomt, zijn deze randapparaten
impliciet aanwezig en hoeven zij niet te worden benoemd in
Invoer/Uitvoer opdrachten. Een voorbeeld zal dit duidelijk maken.
#include /* standard Invoer/Uitvoer header file */
main ()
{
char letter;
printf ("Tik een letter in (X stopt het programma):\n");
do {
letter = getchar (); /* haal een letter op */
putchar (letter); /* en druk hem af ... */
} while ((letter != 'x') && (letter != 'X'));
printf ("\nEinde programma.\n");
}
Het eerste wat opvalt is de eerste regel van het programma met als
tekst #include . Deze lijkt wel een beetje op de
#define die we reeds bestudeerd hebben. In plaats van
vervanging van tekst wordt met deze opdracht een compleet bestand
ingelezen. Het systeem gaat op zoek naar een bestand met de naam
stdio.h en leest de inhoud ervan op de plaats van het
statement. Het zal duidelijk zijn dat stdio.h correcte C
statements moet bevatten die gecompileerd kunnen worden als deel van
het programma. Het stdio.h bestand wordt geleverd met de
compiler en bevat een hoeveelheid #define statements die
enkele Invoer/Uitvoer bewerkingen definiëren.
Dit bestand wordt een C header file genoemd en je zult een
flink aantal van dit soort bestanden vinden in de subdirectories die
behoren bij de compiler. Elke header file heeft een specifieke
toepassing en je kunt er een of meerdere toevoegen aan je programma.
De meeste C compilers gebruiken de aanhalings tekens (") om aan te
geven dat het bestand kan worden gevonden in de current
subdirectory. Het kleiner dan teken (<) en het groter dan teken
(>) worden vervolgens gebruikt om aan te geven dat het bestand kan
worden gevonden in het include path, hetgeen wordt
aangegeven door middel van het MS-DOS PATH commando of de
SET INCLUDE= instructie.
Invoer/Uitvoer operaties in C
In de programmeertaal C maken Invoer- en Uitvoer funkties geen deel uit
van de taal. Ze moeten door de gebruiker zelf worden gedefinieerd. Om
niet iedereen het wiel opnieuw uit te laten vinden, hebben de
schrijvers van de C compiler al het een en ander gedaan. Verschillende
Invoer- en Uitvoer funkties worden derhalve bij de compiler geleverd,
via een funktie bibliotheek, en kunnen direct worden toegepast bij het
schrijven van je programma's. Veel fabrikanten van compilers hanteren
voor deze funkties dezelfde namen, waardoor ze een soort standaard zijn
geworden. Ook plaats en datatype van de funktie parameters zijn zeer
consistent. Programma's zijn daardoor overdraagbaar naar een andere
compiler.
Trek eens wat extra tijd uit en bestudeer de inhoud van het
stdio.h bestand. Er zullen beslist zaken zijn die je nog
niet begrijpt, maar veel zul je herkennen en je kunt er bijzonder veel
van leren. De naam stdio.h is een afkorting voor
Standaard Input and Output header file en geeft exact de
functionaliteit weer. Dit bestand definieert de In- en Uitvoer funkties
in de vorm van #define's en macro's. Probeer niet meteen
alles te begrijpen wat in dit bestand staat. Dit bestand wordt aan
vrijwel elk programma toegevoegd. Om deze reden wordt er dus wat
uitgebreider op ingegaan.
Als programma's groter worden, worden ze vaak opgesplitst in delen, die
afzonderlijk worden gecompileerd. Sommige zaken zijn echter algemeen en
zullen in elk van de delen voorkomen. Het is verstandig deze algemene
zaken in een apart header bestand op te nemen en dit bestand middels
#include aan elk afzonderlijk programma toe te voegen.
Wanneer er een algemene wijziging doorgevoerd moet worden, dan behoeft
dat slechts op een plaats te gebeuren. Zie voor een concreet voorbeeld
van dit principe Appendix C, "Totaal programma".
Kijk weer naar het voorbeeld programma. Er wordt een character
variabele gedeclareerd en een boodschap afgedrukt middels
printf(). Dan wordt een eindeloze lus gestart, die eindigt
zodra we de letter x (of X) intoetsen.
Als je niet meer weet hoe iteraties werken, kijk dan nog eens naar
Hoofdstuk 3, "Programma besturing". Twee nieuwe funkties worden
geïntroduceerd, een om een teken van het toetsenbord te lezen
en een om een teken af te drukken.
De funktie getchar() leest een enkel teken van het standaard
invoer apparaat, het toetsenbord, en kent dat toe aan de variabele
letter. De funktie putchar() gebruikt het
standaard uitvoer apparaat, het scherm, en drukt de letter die in
letter is opgeslagen af. De lus blijft letters lezen en
afdrukken totdat we een kleine- of hoofdletter x intikken.
Het inlezen van getallen
Bestudeer nu het programma GETALIN.C. Het programma is vrijwel identiek
aan het vorige. Dit programma werkt met een integer en eindigt wanneer
de integer de waarde 100 krijgt.
#include /* standard Invoer/Uitvoer header file */
main ()
{
int getal;
printf ("Tik een getal in (100 stopt het programma):\n");
do {
scanf ("%d", &getal); /* lees een getal in */
printf ("Het getal is %d\n", getal); /* en druk hem af ... */
} while (getal != 100);
printf ("Einde programma.\n");
}
In plaats van teken voor teken lezen we hier een getalswaarde in. Dit
doen we met behulp van de funktie scanf(). Deze funktie is
het broertje van printf() en is een Invoer funktie in plaats
van een Uitvoer funktie. De funktie scanf() geeft als
parameter het adres van de integer variabele mee, zodat bij terugkeer
de gelezen waarde niet verloren gaat. Lees als je dit niet begrijpt het
vorige hoofdstuk over pointers nog eens na. Een funktie moet het adres
van een variabele hebben om de waarde terug te kunnen geven aan het
aanroepend programma.
De funktie scanf() doorloopt de ingevoerde regel totdat de
eerste gegevens die met het formaat corresponderen worden gevonden.
Voorloop spaties worden genegeerd en in dit geval worden alleen cijfers
gelezen totdat opnieuw een spatie wordt gevonden of een teken dat geen
cijfer is.
Er wordt dus een complete regel gelezen, d.w.z. alle tekens die werden
ingetikt tot aan de ENTER toets. Het toetsenbord buffer
geldt als invoer en wordt doorzocht. Elke aaneengesloten serie cijfers
wordt als getal gezien, hetgeen betekent dat er tevens een conversie
plaatsvindt van tekst naar numeriek. Wanneer je meerdere getallen op
een regel tegelijk intikt (gescheiden door een spatie), zal de iteratie
ervoor zorgen dat elk getal netjes wordt geconverteerd en afgedrukt.
Het getal 100 laat het programma eindigen. Wanneer het getal 100 ergens
in het midden van een serie getallen werd ingetikt, zullen alle
getallen voor het getal 100 en het getal 100 zelf worden afgedrukt. De
rest wordt niet meer verwerkt.
Denk eraan dat het getal dat wordt ingetikt binnen het waardebereik van
de integer moet liggen. Dit is machine afhankelijk. Vroeger was een
integer altijd 16 bits. Met de komst van Intel 386 en 486 processoren
is dat 32 bits geworden. Het is de verantwoordelijkheid van de C
programmeur te bewaken dat ingevoerde gegevens in dit waardebereik
liggen. Compileer en test nu het programma. Probeer het waardebereik
uit.
Het inlezen van teksten
Bestudeer het programma TEKSTIN.C. Dit programma leest een tekst in. Ook dit programma
is vrijwel identiek aan het vorige, echter nu is een buffer voor maximaal
80 tekens gedeclareerd (het 81ste is voor het NULL karakter). De parameter
tekst in de scanf() wordt niet vooraf gegaan door een ampersand
(&) omdat het een tabel variabele is. Deze is per definitie een pointer.
Compileer en test dit programma. #include /* standard Invoer/Uitvoer
header file */ main () { char tekst[81]; printf ("Tik een tekst in, niet
meer dan 80 tekens.\n"); printf ("Als de eerste letter een X is, stopt het
programma.\n"); do { scanf ("%s", tekst); /* lees een tekst in */ printf
("De tekst is '%s'\n", tekst); /* en druk die af ... */ } while ((*tekst
!= 'x') && (*tekst != 'X')); printf ("Einde programma.\n"); } Wellicht
werd je verrast door hetgeen werd afgedrukt. De regel tekst die je intikte,
werd opgedeeld in aparte woorden. Als scanf() strings moet verwerken
(%s), worden tekens verwerkt totdat ofwel een spatie wordt gelezen of het
einde van de regel wordt bereikt. Concreet komt dit overeen met het lezen
van woorden. Vanwege de iteratie worden dus alle woorden uit de ingetikte
regel gelezen en afgedrukt. Het programma eindigt zodra het de letter x
op de eerste positie vindt. Door de itererende werking zal het programma
stoppen bij het eerste woord dat met een x begint. Tik eens
een regel in met 5 woorden waarvan de derde begint met een x.
Je zult zien dat de eerste drie worden afgedrukt, terwijl de laatste 2 woorden
niet meer worden verwerkt.
Probeer ook eens meer dan 80 tekens in te voeren. Het hangt van je
computersysteem af wat er gebeurt. In de meeste gevallen zul je de
computer opnieuw moeten starten! Ook hier ligt er een zekere
verantwoordelijkheid bij de C programmeur.
Invoer/Uitvoer in het geheugen
Het volgende voorbeeld zal in eerste instantie wat vreemd aandoen.
Naarmate je meer ervaring krijgt, zul je veel toepassingen vinden waar
je het principe kunt gebruiken. Bestudeer het programma INMEM.C, een
programma dat leest en afdrukt in het geheugen.
main ()
{
int DS1, DS2, DS3, DS4, DS5; /* vijf dobbelstenen */
char regel[8];
DS1 = 2; DS2 = 3; DS3 = 4; DS4 = 5; DS5 = 6; /* initialiseer */
sprintf (regel, "%d %d %d %d %d\n", DS1, DS2, DS3, DS4, DS5);
printf ("1: %s", regel);
sscanf (regel, "%d %d %d %d %d\n", &DS5, &DS4, &DS3, &DS2, &DS1);
printf ("2: %d %d %d %d %d\n", DS1, DS2, DS3, DS4, DS5);
}
Het programma is een aangepast stuk coding uit het Totaal Programma. Er
worden vijf integers gedeclareerd en een tekst buffer. De integers zijn
de vijf dobbelstenen, deze krijgen een initiële waarde. Vervolgens
worden de waarden van de dobbelstenen in de tekst buffer afgedrukt in
plaats van op het scherm. Dit gebeurt met de sprintf()
funktie. Deze funktie werkt op precies dezelfde manier als de
printf() funktie. Er is een parameter meer, de pointer naar
de buffer, hier de variabele regel. Ter controle drukken we
de buffer af met behulp van printf().
Omdat we in het geheugen hebben afgedrukt, is de regel nog beschikbaar
voor verwerking. We kunnen de regel dus lezen middels
sscanf() het broertje van scanf(). Ook deze
funktie heeft een parameter meer, de pointer naar de invoer buffer,
hier de variabele regel. Net als bij scanf()
moeten we de funktie de adressen geven van de integer variabelen waarin
we de waarden willen lezen. Om aan te tonen dat de oorspronkelijke
dobbelstenen van waarde veranderen, worden ze in omgekeerde volgorde
terug gelezen. Ter controle wordt een en ander vervolgens afgedrukt.
Het voorbeeld is ter illustratie en niet erg zinvol. Het laat het
principe zien. Je zult naarmate je meer en meer gaat programmeren in C
de charme ervan ontdekken en met name sprintf() vaak
toepassen. Je kunt er immers de inhoud van een string mee wijzigen!
Standaard fout afhandeling
Soms is het wenselijk de standaard uitvoer van een programma naar
believen te kunnen sturen (redirection), bijvoorbeeld naar
een bestand. De foutmeldingen echter wil je meestal wel op het
beeldscherm zien verschijnen. Daar is een mogelijkheid toe. Een
voorbeeld hiervan staat is het programma FOUTJE.C. Bestudeer dit
programma nu.
#include /* standard Invoer/Uitvoer header file */
main ()
{
int index;
for (index = 0; index < 6; index++) {
printf ("Deze regel gaat naar de standaard uitvoer.\n");
fprintf (stderr, "Deze regel gaat naar de fout uitgang.\n");
}
exit (99);
}
Het programma bevat een iteratie van twee uitvoer berichten, de ene
naar standaard uitvoer, de andere naar de standaard fout uitgang. Het
bericht naar de standaard fout uitgang wordt afgedrukt middels de
fprintf funktie en bevat de apparaat naam stderr
als de eerste parameter. Deze funktie werkt verder exact hetzelfde als
de inmiddels zo vertrouwde printf funktie. Compileer en test
dit programma.
Wanneer je intikte:
FOUTJE
kreeg je 12 regels uitvoer op je scherm.
Wanneer je intikt:
FOUTJE > REGELS.DAT
krijg je 6 regels (fout) uitvoer op je scherm en 6 regels
(standaard) uitvoer in het bestand REGELS.DAT. Probeer dit eens uit.
Tenslotte het laatste statement uit het voorbeeld programma. De exit()
funktie sluit het programma af en geeft de waarde van de meegegeven parameter
terug aan het Operation System. Onder MS-DOS kan deze waarde worden getest
met de MS-DOS opdracht IF ERRORLEVEL. In het kader van de cursus
gaan we hier niet verder op in.
|