|
Strings en Tabellen
Een beschrijving van de string
Een string is een groepje karakters uit de tekenset van de computer,
meestal letters, cijfers en leestekens. Om computeruitvoer zinvol te
kunnen presenteren aan gebruikers, moeten de interne gegevens vertaald
worden naar teksten. Als programmeur wil je op niet al te omslachtige
wijze met teksten en de opmaak daarvan kunnen werken. Eigenlijk werk je
al vanaf het eerste begin met teksten. Het Hello World
programma uit Hoofdstuk 1, "De eerste stap" bevat de string "Hello, world
...". De complete definitie van een string is een rij tekens van
het type char afgesloten door het NULL karakter. Het NULL
karakter is het nulde karakter uit de tekenset, ofwel
zero, ofwel hexadecimaal nul, ofwel 0x00, ofwel '\0'.
Funkties die met strings werken, zullen alle teken voor teken uit de
string behandelen tot het eerste NULL karakter. Dit kunnen funkties
zijn voor bijvoorbeeld vergelijken, afdrukken of kopiëren.
Een tabel is een aaneengesloten stuk met gegevens die alle van
hetzelfde type zijn. Dit type kan een complex type zijn, zoals we
zullen zien als we de structuren gaan behandelen. Een string is dus
een bijzonder geval van een tabel, een tabel van karakters.
De beste manier om dit te begrijpen is door middel van voorbeelden.
Bestudeer daarom eens programma CHARSTR.C. Nieuw is hier de declaratie
van de variabele titel, als tabel van karakters.
main ()
{
char titel[8];
titel[0] = 'Y';
titel[1] = 'a';
titel[2] = 'h';
titel[3] = 't';
titel[4] = 'z';
titel[5] = 'e';
titel[6] = 'e';
titel[7] = '\0';
printf ("De naam van het spel is '%s'\n", titel);
printf ("Een letter uit de naam is '%c'\n", titel[3]);
printf ("Een deel van de naam is '%s'\n", &titel[4]);
}
De blokhaken definiëren een tabel met elementen in C. In het voorbeeld
programma staat er het getal 8 tussen, hetgeen betekent dat dit een
tabel is met 8 elementen van het type char. In de
programmeertaal C is het eerste element van een tabel het element met
subscript 0, het tweede element heeft subscript 1 en zo verder. Het
laatste element heeft als subscript een minder dan het aantal elementen
van de tabel, in dit geval dus 7. De tabel heeft dus 8 elementen,
genaamd titel[0], titel[1]
tot en met titel[7]. Onthoud dus goed dat in C de
subscripts lopen van 0 tot en met een minder dan het aantal elementen
dat de tabel groot is!
Toepassing van strings
De variabele titel is dus een string met een grootte van 8
karakters. Omdat het NULL karakter echter ook een positie inneemt,
blijft er netto slechts ruimte over voor 7 tekens. De tabel wordt
gevuld met behulp van 8 toekenningen, voor elk element van de tabel
een. Er worden de zeven letters van het woord Yahtzee in de
tabel geplaatst en tenslotte nog de numerieke 0 als de end-of-string
indicator. Het zal duidelijk zijn dat hier het NULL karakter bedoeld
wordt en niet zomaar een nul. De string kan worden afgedrukt, tezamen
met wat andere tekst middels printf().
De combinatie %s is de definitie voor het afdrukken van een
string. Het systeem zal teken voor teken uit de string afdrukken tot
het NULL karakter wordt gelezen. Merk op dat in het printf
statement de naam van de tabel, titel, wordt meegegeven
zonder subscript.
De tweede printf() laat zien dat het mogelijk is om met
behulp van de %c combinatie en de referentie naar het
specifieke element een enkel teken uit een tabel af te drukken. De
laatste printf() laat zien hoe je een deel van een string
kunt afdrukken. Dit doe je door het element, waar het afdrukken moet
beginnen, te benoemen middels zijn subscript. Het & teken
wijst het adres aan waar dit teken in het geheugen staat. Over dit
principe zal straks meer wordt verteld; het is goed om er nu vast
kennis mee te maken.
Het voorbeeld zou je het idee kunnen geven dat omgaan met strings in C
nogal omslachtig is, omdat je strings teken voor teken moet behandelen.
Niets is minder waar, zoals we in het volgende programma zullen zien.
Compileer en test nu eerst dit programma.
String funkties
Bestudeer het programma STRINGS.C om te zien hoe in C met strings kan
worden omgegaan. Eerst worden twee string gedeclareerd, waarmee kan
worden gewerkt. Dan komen we bij de statements waar we een nieuwe,
veel gebruikte funktie vinden, de strcpy() funktie ofwel de
string copy.
main ()
{
char spel1[20], spel2[20];
strcpy (spel1, "E N E N");
strcpy (spel2, "T W E E E N");
printf ("De naam van het eerste spel is: %s\n", spel1);
printf ("De naam van het tweede spel is: %s\n", spel2);
printf ("Bij vergelijking van de namen is de grootste: %s\n",
(strcmp (spel1, spel2) > 0 ? spel1 : spel2));
strcpy (spel1, "THREE ");
strcat (spel1, "OF A ");
strcat (spel1, "KIND");
printf ("De naam van een spel is: %s\n", spel1);
}
Deze funktie kopieert vanuit een gegeven string naar een andere tot het
NULL karakter. Het is eenvoudig te onthouden welke gekopieerd wordt en
welke ontvangt als je denkt aan de toekenning.
Als je schrijft a = 3;, dan wordt 3 gekopieerd in a, dus a ontvangt.
Bij strcpy gaat het eender. Als je schrijft
strcpy(a,"drie");, dan wordt de tekst drie
gekopieerd in a, dus a ontvangt. De aanhalingstekens blijven
achterwege, ze zijn voor de C compiler een aanwijzing dat met een
string wordt gewerkt.
In de voorbeelden wordt E N E N toegekend aan
spel1 en T W E E E N aan spel2.
Beide teksten worden afgedrukt. Merk op dat het niet nodig is de tabel
precies zo groot te maken als de string die er aan wordt toegekend. De
regel in C is dat een tabel minstens zo lang moet zijn als de lengte
van de grootste tekst die er aan wordt toegekend plus een (voor het
NULL karakter).
Dan nog even dit. Met 'a' wordt een karakter aangeduid (8
bits) in het geheugen. Met "a" worden twee karakters in het
geheugen aangeduid, een met de a en een met de NULL.
We kunnen strings ook met elkaar vergelijken. Daarvoor wordt bij de
compiler de strcmp() funktie geleverd, de string compare.
Deze funktie geeft een waarde af als resultaat van een vergelijking
tussen twee strings. Als de eerste string (alfabetisch) groter is dan
de tweede is de funktie waarde 1, als ze gelijk zijn 0 en als de eerste
kleiner is dan de tweede -1. De derde printf() laat dit
zien. Een T is groter dan een E dus zal
T W E E E N worden afgedrukt.
Denk eraan dat grote en kleine letters verschil maken. Een
a is groter dan een Z!
De laatste vier statements laten zien hoe in C een string uit delen kan
worden opgebouwd. De bibliotheek bevat daartoe de strcat()
funktie, de string concatenation. Deze funktie plakt als het ware de
ene string achter de andere, daarbij zorg dragend voor de juiste plaats
van het NULL karakter. In het voorbeeld wordt spel1 als
volgt opgebouwd: De tekst THREE wordt gekopieerd
en staat dus vooraan, inclusief afsluitend NULL karakter. Vervolgens
wordt het NULL karakter verwijderd en de tekst OF A
er aan vast geplakt, met NULL karakter. Tenslotte wordt nogmaals het
NULL karakter verwijderd en de tekst KIND er aan vast
geplakt, met afsluiten NULL karakter. Het resultaat, THREE OF A
KIND wordt afgedrukt.
Strings zijn niet moeilijk toe te passen en bijzonder handig. Besteed
wat tijd om er mee vertrouwd te raken. Compileer en test dan dit
programma en verklaar voor jezelf de resultaten.
Numerieke tabellen
Bestudeer het programma TABELINT.C. Het bevat een voorbeeld van een
tabel van integers. Merk op dat de tabel gedeclareerd wordt op een
manier overeenkomstig die van de string.
main ()
{
int waarde[12];
int index;
for (index = 0; index < 12; index++)
waarde[index] = (3 * index + 5);
for (index = 0; index < 12; index++)
printf ("De waarde van element %2d is %3d\n",
index, waarde[index]);
}
We hebben hier 12 integer variabelen om mee te werken. De namen van de
variabelen zijn waarde[0],
waarde[1] enzovoort tot en met
waarde[11]. Binnen een for lus krijgt ieder van
deze variabelen een fantasie waarde. Een tweede iteratie drukt de
toegekende waarden af ter controle. Als je al het vorige begrepen hebt,
zal dit programma geen probleem voor je zijn. Compileer en test het nu.
Evenzo kun je tabellen declareren met floating point variabelen.
Bestudeer het programma TABEL.C voor een voorbeeld.
main ()
{
int index;
int punten[12];
float zomaar[12];
char titel[] = "Yahtzee - pc3270";
for (index = 0; index < 12; index++) {
punten[index] = index * 30;
zomaar[index] = 12.345 * (index + 23);
}
printf ("%s:\n\n", titel);
for (index = 0; index < 12; index++) {
printf ("Spel %2d: %4d punten (%7.3f)\n",
index, punten[index], zomaar[index]);
}
}
Het programma laat ook zien hoe strings kunnen worden geïnitialiseerd.
De variabele titel wordt gedeclareerd als string van
onbepaalde lengte, door niets tussen de blokhaken te zetten. De
compiler zal het aantal tekens tellen en precies genoeg ruimte in het
geheugen reserveren.
Verder bevat het programma geen nieuwe zaken. Ook hier krijgen de
variabelen fantasie waarden en wordt alles ter controle afgedrukt.
Compileer en test dit programma.
Funktie aanroep met tabel als parameter
Er is een mogelijkheid om gegevens terug te krijgen van een funktie met
behulp van een tabel. In Hoofdstuk 5, "Funkties en Variabelen" ging
het steeds om enkelvoudige variabelen. Bestudeer nu programma GEEFWEER.C
voor een bespreking.
#define GROOTTE 8 /* Aantal elementen van tabel */
main ()
{
int index;
int matrix[GROOTTE];
for (index = 0; index < GROOTTE; index++) /* vul de tabel */
matrix[index] = index + 10;
for (index = 0; index < GROOTTE; index++) /* druk origineel af */
printf ("Originele tabel (main): element %d is %d\n",
index, matrix[index]);
verander (matrix); /* verander de tabel via een funktie */
for (index = 0; index < GROOTTE; index++) /* druk veranderde af */
printf ("Veranderde tabel (main): element %d is %d\n",
index, matrix[index]);
}
verander (lijst)
int lijst[];
{
int i;
for (i = 0; i < GROOTTE; i++) /* druk origineel af */
printf ("Originele tabel (verander): element %d is %d\n",
i, lijst[i]);
for (i = 0; i < GROOTTE; i++) /* tel overal 10 bij op */
lijst[i] += 10;
for (i = 0; i < GROOTTE; i++) /* druk veranderde af */
printf ("Veranderde tabel (verander): element %d is %d\n",
i, lijst[i]);
}
In dit programma wordt een tabel met de naam matrix
gedeclareerd. Het aantal elementen van de tabel ligt vast in een
#define en is hier gesteld op 8.
De tabel elementen krijgen een fantasie waarde. Deze waarden worden
afgedrukt. Vervolgens wordt de funktie verander()
aangeroepen, waarbij de gehele tabel als parameter wordt meegegeven.
De funktie verander() werkt vervolgens met de variabele
lijst, die gedeclareerd wordt als een tabel van integers met
onbepaalde grootte. Het is niet nodig aan de funktie mee te delen
hoeveel elementen de tabel heeft, maar het mag wel. In het algemeen
verwerken funkties tabellen totdat een of andere end-of-data
indicatie wordt gevonden, zoals het NULL karakter bij strings, of een
vooraf gedefinieerd teken of een patroon van karakters. Vaak ook wordt
er een aanvullende parameter meegegeven aan de funktie, die het aantal
elementen in de tabel bevat. In dit voorbeeld wordt gewerkt met een
symbolische constante.
Tot zover is er nog niets nieuws in vergelijking met wat we reeds
eerder geleerd hebben. Wat wel nieuw is, is dat we met die ene
parameter acht integers aan de funktie hebben doorgegeven.. Om dat aan
te tonen, worden in de funktie nogmaals alle elementen van de tabel
afgedrukt. Vervolgens wordt bij elk tabel element 10 opgeteld. De tabel
wordt weer afgedrukt, nu met de veranderde inhoud. De funktie keert
terug naar het hoofdprogramma. Weer worden hier alle elementen een voor
een afgedrukt. We zien dat de inhoud van de tabel inderdaad gewijzigd
is in de funktie. Bij terugkeer naar het hoofdprogramma zijn die
wijzigingen niet verloren gegaan. Compileer en test het programma om
te controleren of het waar is.
We hebben bij het bestuderen van funkties gezegd, dat bij de parameter
overdracht, binnen de funktie met een kopie van de parameters wordt
gewerkt die, zodra de funktie eindigt, wordt weggegooid. Dat is niet
het geval met tabellen. De tabel zelf wordt doorgegeven en de funktie
kan deze naar believen veranderen. Het resultaat van de veranderingen
blijft derhalve beschikbaar voor het aanroepende programma. Het lijkt
wellicht vreemd dat tabellen anders worden behandeld dan enkelvoudige
variabelen, straks zal blijken dat dit eigenlijk niet het geval is.
Voor een verklaring zul je moeten wachten tot we de pointers gaan
behandelen.
Een andere manier om gegevens met funkties uit te wisselen is namelijk
met behulp van pointers. We zullen ontdekken dat een tabel in
werkelijkheid een pointer is naar een lijst van variabelen. Maak je
daar nu echter nog geen zorgen om. Probeer nu eerst de basis principes
van tabellen onder de knie te krijgen. Compileer en test daarom nu het
programma.
Meerdimensionale tabellen
Bestudeer nu het programma VAKJES.C waarin opgenomen een
tweedimensionale tabel. Deze tabel wordt ook gebruikt in het totaal
programma YAHTZEE.C.
main ()
{
int kolom, spel;
int vakje[4][14];
for (kolom = 1; kolom < 4; kolom++) /* vul de tabel */
for (spel = 1; spel < 14; spel++)
vakje[kolom][spel] = kolom * 100 + spel;
vakje[2][6] = -1; /* verander een specifieke */
vakje[2][2] = vakje[2][6]; /* kopieeer een vakje */
for (spel = 1; spel < 14; spel++) { /* druk tabel af */
printf ("Spel %2d:", spel);
for (kolom = 1; kolom < 4; kolom++)
printf ("%8d", vakje[kolom][spel]);
printf ("\n");
}
}
De variabele vakjes is een tweedimensionale tabel, bestaande
uit 4 kolommen van elk 14 spelen (integers), in totaal dus 64 integers.
Het eerste element is vakje[0][0], het
laatste is vakje[3][13]. De tabel wordt
gevuld met getallen, kolom 1 met honderdtallen, kolom 2 met
tweehonderdtallen en kolom3 met driehonderdtallen. Twee willekeurige
elementen krijgen nog een bijzondere waarde, om te laten zien hoe een
individueel element kan worden aangewezen.
De tabel wordt niet in zijn geheel afgedrukt. Slechts het deel wat een
waarde heeft gekregen. Kolom 0 (nul) wordt in zijn geheel overgeslagen.
Van elke kolom wordt tevens spel 0 (nul) overgeslagen. In dit programma
is er voor gekozen het subscript en de plaats die een vakje op het scherm
inneemt 1:1 te houden. Normaal reserveer je in C net zoveel kolommen als
je nodig hebt. We zouden dan declareren vakje[3][13], met als subscript
0, 1 en 2 voor de kolommen en 0 tot en met 12 voor de spelen. Op het scherm
staat echter kolom 1, 2 en 3 met spel 1 tot en met 13. Door nu extra ruimte
te nemen, kan toch met subscript 1, 2 en 3 voor de kolommen en 1 tot en
met 13 voor de spelen worden gewerkt.
|