C-taal voor beginners - hoofdstuk 3
strings en arrays
[3.1 strings] [3.2 arrays] [3.3 het gebruik van een string] [3.4 een deel van een string outputten] [3.5 stringfuncties] [3.6 een array van integers] [3.7 strings als input] [3.8 multi-dimensionele arrays]
Een string is een groep karakters, meestal letters van het alfabet. Om een goed ogende output te verkrijgen, die betekenisvolle namen en titels heeft en esthetische voldoening geeft, heeft u de de mogelijkheid nodig om tekstuele data te kunnen verwerken. Een exacte definitie van een string is "een reeks data van het type char, afgesloten door een null karakter". Wanneer C een string gaat gebruiken, kopiëren, of wat dan ook, worden de functies zo opgesteld dat ze doen wat er gevraagd wordt dat ze doen, totdat er een nul karakter gededecteerd wordt. Zulk een string noemt men in vaktermen een ASCII-Z string. Let op: alhoewel in negen van de tien gevallen de NULL macro werkelijk op 0 staat ingesteld, is dit niet bij elke compiler zo. Dus om echt iets op niets in te stellen kan u beter de macro gebruiken dan de 0 zelf. Let op de macro NULL is niet hetzelfde als het nulkarakter '\0'. Zoals u in een later hoofdstuk zal leren is de NULL macro handig om bepaalde dingen "naar niets te laten wijzen". Het afsluitende karakter van een string is echter het nul karakter, dit is '\0'.
Een array is een reeks van homogene stukken data, allemaal identiek in type. Dit type kan wel zeer complex zijn. Een string is simpelweg een speciaal array, een reeks data van het type char.
De beste manier om deze principes te bekijken is bij wijze van voorbeeld:
#include<stdio.h>int main(void) { char naam[5]; /* definitie van een string van karakters */naam[0] = 'J' ; naam[1] = 'o' ; naam[2] = 'h' ; naam[3] = 'n' ; naam[4] = 0 ; /* nul karakter = einde van de tekst, u kan ook '\0' gebruiken */printf("De naam is %s\n", naam); printf("Eén letter is %c\n", naam[2]); printf("Een deel van de naam is %s\n", &naam[1]);return 0; }
Het eerste nieuw stukje code is char naam[5]; Dit definieert een string van het char type. De vierkante haken definiëren een array subscript in C en de 5 tussen de vierkante haken definieert vijf data velden van het type char, allemaal deel van de string variabele naam. In de C-taal beginnen alle subscripts met 0. We hebben hier vijf char type variabelen: naam[0], naam[1], naam[2], naam[3], naam[4]. U mag niet vergeten dat in C de subcripts gaan van 0 tot 1 minder dan het nummer in de definitie (tussen de vierkante haken). Dit werd zo origineel bepaald in de C-taal en kan niet door de programmeur veranderd worden.
3.3 het gebruik van een string
De variabele naam is dus een string met ruimte voor vijf karakters, maar aangezien we ruimte nodig hebben voor het afsluitende NULL karakter dat meetelt als één van de vijf karakters, hebben we eigenlijk maar vier bruikbare karakters. Om iets in de string te laden hebben we vijf toekenningsstatements gebruikt, welke elk één karakter toekent aan één van de stringkarakters. Uiteindelijk wordt de laatste plaats van de string gevuld met de numeral 0 als de eindindicator en onze string is compleet. Nu dat we hem volledig gedefinieerd hebben drukken we deze af d.m.v. printf("De naam is %s\n", naam); De %s is de outputdefinitie die gebruikt wordt voor een string. Het programma zal karakters printen, beginnend met het eerste in de string naam, totdat het nulkarakter gevonden wordt. Dit wordt zelf niet afgedrukt. Merk op dat in het printf() statement enkel de naam van de variabele gegeven moet worden, zonder subscript, aangezien we de ganse string willen afdrukken, beginnend bij het begin. Het is belangrijk te weten dat de variabele naam op zichzelf naar de ganse string verwijst, maar dat naam[ ] met een waarde tussen de vierkante haken naar één enkel karakter in de string verwijst.
3.4 een deel van een string outputten
printf("Eén letter is %c\n", naam[2]); maakt duidelijk dat we elk afzonderlijk karakter van de string kunnen afdrukken door %c te gebruiken en het individuele karakter van de variabele naam te benoemen, samen met het subscript. In hoofdstuk 1 leerden we reeds dat %c een karakter aanduid en dat een karakter slechts één teken is. printf("Een deel van de naam is %s\n", &naam[1]); illustreert hoe we een een deel van de string kunnen afdrukken, door het begin aan te duiden met een subscript. Het & teken specifieert hier het adres van naam[1]. Dit wordt uitvoerig bestudeerd in het volgende hoofdstuk, dus maak er u nu niet al te veel zorgen over.
Het voorbeeldprogramma dat we tot nu toe gebruikten zal u waarschijnlijk de indruk geven dat strings nogal omslachtig zijn, aangezien elk karakter één voor één geïnitialiseerd moet worden. Het zou moeilijk werken zijn als we een string op die manier moesten gebruiken, maar ik deed het tot nu toe om duidelijk te maken hoe het in elkaar zit. Het volgende voorbeeld laat zien dat strings in feite zeer gemakkelijk in gebruik zijn.
#include <stdio.h> #include <string.h> int main(void) { char naam1[12], naam2[12], mixed[25]; char titel[20]; strcpy(naam1, "Rosalinda"); strcpy(naam2, "Tom"); strcpy(titel, "Dit is de titel."); printf(" %s\n\n", titel); printf("Naam 1 is %s\n", naam1); printf("Naam 2 is %s\n", naam2); if(strcmp(naam1, naam2) > 0) /* geeft 1 als naam1 > naam2 */ strcpy(mixed, naam1); else strcpy(mixed, naam2); printf("De grootste alfabetische naam is %s\n", mixed); strcpy(mixed, naam1); strcat(mixed, " "); strcat(mixed, naam2); printf("Beide namen zijn %s\n", mixed);return 0; }
Dit geeft als output:
Dit is de titel. Naam 1 is Rosalinda Naam 2 is Tom De grootste alfabetische naam is Tom Beide namen zijn Rosalinda Tom
Om de verschillende stringfuncties te kunnen gebruiken includeren we de header string.h in ons programma.
3.5.1 strcpy()
Dit programma is een goed voorbeeld van het gebruik van een paar stringfuncties. Eerst declareren we 4 strings. Vervolgens komen we aan een nieuwe functie die u zeer nuttig zal vinden, de strcpy() functie, of string-kopieerfunctie. Deze kopieert van één string naar een andere totdat het null karakter gevonden wordt in de bronstring. Herinner u dat de null eigenlijk een 0 is die door het systeem aan de string wordt toegevoegd. Het is duidelijk welke string gekopieerd wordt als u het bekijkt als een toekennings-statement. Net als bij, bijvoorbeeld, x = 23; wordt de data van rechts naar links gekopieerd, zodat in ons voorbeeld na de uitvoering van het eerste statement de variabele naam1 de string "Rosalinda" bevat, maar zonder de dubbele quotes " ", deze zijn gewoon een manier voor de compiler om te weten dat hij met strings te maken heeft. Het zou duidelijk moeten zijn dat strcpy(naam1, "Rosalinda"); een stringconstante naar een string variabele kopieert.
Op dezelfde manier wordt de string "Tom" naar naam2 gekopieerd, daarna wordt de titelstring gekopieerd naar de string genaamd titel. De titel en allebei de namen worden dan afgedrukt. Merk op dat het niet noodzakelijk is voor de ontvangende string om exact dezelfde grootte te zijn als de string die het moet herbergen, zolang hij maar tenminste zolang is als de bronstring plus één karakter meer voor de afsluitende null.
3.5.2 strcmp()
De volgende functie die we zullen bekijken is strcmp(), ofwel de string-vergelijkingsfunctie. strcmp(naam1, naam2) geeft een 1 indien de eerste string groter is dan de tweede, een 0 als de twee gelijk zijn van lengte en dezelfde karakters bevatten en een -1 als de eerste string kleiner is dan de tweede. In ons voorbeeld wordt één van de twee strings, afhankelijk van de strcmp(), gekopieerd in de string variabele mixed en de grootste naam wordt afgedrukt met een printf() statement. Het is duidelijk dat "Tom" alfabetisch groter is. De lengte doet er niet toe, enkel de relatieve positie in het alfabet. Het resultaat is ook afhankelijk van het feit dat het hoofdletters of kleine letters zijn.
3.5.3 strcat()
U merkt ook nog een derde functie in het voorbeeld: strcat(), ofwel de stringconcatenatie. Deze voegt simpelweg de karakters van een string aan het einde van een andere string toe, automatisch de null aanpassend, zodat alles in orde is. In dit geval wordt naam1 naar mixed gekopieerd, daarna worden er twee blanco's aan toegevoegd, om dan de inhoud van naam2 eraan toe te voegen. Het resultaat wordt afgedrukt met een printf() functie.
Strings zijn niet moeilijk te gebruiken en zijn zeer nuttig, maar ze vereisen een zekere aandacht. Het is verkeerd een string naar een string te kopiëren, die korter werd gedefinieerd dan de bronstring, maar de compiler zal de kopieeropdracht toch uitvoeren, waardoor andere data waarschijnlijk overschreven wordt. De compiler kent geen manier om u hiervoor te waarschuwen, dus een beetje voorzichtigheid is geboden.
Een snelle controle van de documentatie van één compiler toonde zo'n achttien stringfuncties beschikbaar voor gebruik. Sommige worden gebruikt om strings te kopiëren met bovengrenzen op het aantal te kopiëren karakters. Er zijn stringfuncties om naar bepaalde karakters in een string te zoeken, andere tellen karakters bij op bepaalde plaatsen. Natuurlijk kan u ook karakters verwijderen, op de plaats waar u dat wilt. Het zou u veel opleveren als u de referenties en documentatie van uw compiler raadpleegde, om te kijken welke functies er beschikbaar zijn. Anders zou u misschien ingewikkelde procedures moeten bedenken om bepaalde zaken te verwezenlijken, die eigenlijk op een gemakkelijke manier tot een goed einde gebracht kunnen worden.
3.5.4 strlen()
Deze functie kan u gebruiken om de lengte van een string te berekenen:
#include <stdio.h> #include <string.h> main(void) { char string[50] = "Een voorbeeldstring"; printf("De string bevat is %d tekens\n", strlen(string));return 0; }
Het null karakter wordt niet meegeteld.
U wist reeds dat de index (lengte of grootte) groter mag zijn dan werkelijke lengte, maar niet kleiner. Als u geen index definieert, zal de compiler zelf de karakters moeten tellen en geheugenruimte voorzien:
char mijn_string[ ] = "Dit is een voorbeeldstring";
Alhoewel dit perfect ANSI-C is, kan deze methode wel problemen geven bij concatenatie en het kopiëren van strings en dergelijke. Op dezelfde manier is ook het volgende mogelijk:
int mijn_array[ ] = {1,23,17,4,-5,100};
3.5.5 strchr()
De functie strchr() scant een string totdat een bepaald karakter gevonden is. Er wordt in voorwaartse richting gezocht. De functie vindt het eerste voorkomen van het karakter in de string. Het null karakter wordt als een deel van de string beschouwd. Een voorbeeld:
int main(void) { char string[17]; char *ptr, c = 'r'; strcpy(string, "Dit is een string"); ptr = strchr(string, c); if (ptr) printf("Het karakter %c staat op positie: %d\n", c, ptr-string); else printf("Het karakter werd niet gevonden\n");return 0; }
3.5.6 gets() en puts()
De twee meest gebruikte stringfuncties zijn waarschijnlijk gets() en puts(). Deze kunnen een volledige string opnemen en outputten, inclusief spaties, tabs, enz... .
gets() verzamelt een string van karakters, totdat het newline
teken '\n' gevonden wordt. Dit wil meestal zeggen totdat de return toets ingedrukt wordt.
Het newline karakter wordt door gets() vervangen door het null karakter.
Alles tot aan de newline wordt in de variabele gestopt, tussen de ronde haken. De gets()
functie is niet lengte-bepaald. Als de inputstring voldoende groot is kan er dus data
overschreven worden.
puts() toont een volledige string op het scherm. Deze functies zijn
handig omdat scanf() en dergelijke problemen ondervinden als er lege
ruimtes in een reeks karakters voorkomen. Ze bevinden zich in de standaardheader, dus string.h
is niet vereist. Ter illustratie twee voorbeelden:
#include <stdio.h> int main(void) { char mijn_string[80]; printf("Geef een string in: "); gets(mijn_string);printf("De string input was: %s\n", mijn_string);return 0; }
#include <stdio.h> int main(void) { char mijn_string[ ] = "Dit is een voorbeeld output string\n"; puts(mijn_string);return 0; }
Een voorbeeld van een programma dat gebruik maakt van een array van integers:
#include<stdio.h>int main(void) { int waarden[12]; int index;for(index=0;index<12;index++) waarden[index] = 2 * (index+4);for(index=0;index<12;index++) printf("De waarde bij index=%2d is %3d\n", index, waarden[index]);return 0; }
Dit geeft als output:
De waarde bij index= 0 is 8 De waarde bij index= 1 is 10 De waarde bij index= 2 is 12 De waarde bij index= 3 is 14 De waarde bij index= 4 is 16 De waarde bij index= 5 is 18 De waarde bij index= 6 is 20 De waarde bij index= 7 is 22 De waarde bij index= 8 is 24 De waarde bij index= 9 is 26 De waarde bij index=10 is 28 De waarde bij index=11 is 30
Merk op dat het array op dezelfde manier als bij een string gedefinieerd wordt, nu is deze echter van het type integer. We hebben dus twaalf integer variabelen om mee te werken, plus één genaamd index. De namen van de variabelen zijn: waarden[0], waarden[1], ..., en waarden[11]. Vervolgens hebben we een for() loop die een boel nonsens toekent, het maakt wel duidelijk dat aan elk van de twaalf variabelen data wordt toegekend, daarna worden ze alle twaalf afgedrukt in de volgende loop. Elk element van het array is simpelweg een integer variabele, geschikt om integer waarden te herbergen. Het enige verschil tussen de variabelen index en, bijvoorbeeld, waarden[2] is de manier waarop ze geadresseerd worden. U zou geen problemen mogen ondervinden bij het begrijpen van dit programma. Compileer en run het en experimenteer wat met de waarden.
Zoals u bij hierboven reeds zag is er nog een mogelijkheid om een string te initialiseren. De groep karakters kan onmiddelijk meegegeven worden aan de variabele:
char mijn_string[30] = "Dit is een voorbeeldstring";
Om een array met integers te initialiseren kan u het volgende doen:
int mijn_array[30] = {1,23,17,4,-5,100};
Deze getallen kunnen ook negatief zijn. Het gebruik van de index (of subscript) is op dezelfde manier als bij een string: 1 staat hier op index 0, 23 op index 1, 17 op index 2, ... .
Tot nu toe hebben we voornamelijk gezien hoe we een string definiëren en als uitvoer verwerken. Om een string in te scannen kan u gewoon het bekende scanf() gebruiken. Het nadeel is echter dat we slechts één woord kunnen opnemen, dus geen string met spaties, zoals dat bij de gets() functie wel het geval is. We gebruiken %s om aan te duiden dat het om een string gaat:
#include<stdio.h>int main(void) { char v_naam[30];printf("Geef uw voornaam in -> "); scanf("%s", &v_naam); printf("Uw voornaam is dus %s", v_naam);return 0; }
Voor een vlugge kijk op multi-dimensionele arrays, bekijken we de volgende initialisatie:
char multi[5][10];
Wat betekent dit? Laten we het in het volgende licht beschouwen:
char multi[5][10];
We bespreken het onderlijnde deel van de "naam" van het array. Met de char er voor en [10] er achter hebben we een array van tien karakters. Maar de naam multi[5] is op zichzelf een array dat aangeeft dat er vijf elementen zijn, die elk op zichzelf een array van tien karakters zijn. We hebben nu dus een array van vijf arrays van tien karakters elk.
Stel dat we dit 2-dimensioneel array met wat willekeurige data gevuld hebben. In het computergeheugen ziet dit er mogelijk uit alsof het gevormd werd door het initialiseren van vijf aparte arrays:
multi[0] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
multi[1] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
multi[2] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'}
multi[3] = {'9', '8', '7', '6', '5', '4', '3', '2', '1', '0'}
multi[4] = {'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A'}
Individuele elementen zijn adreseerbaar op de volgende methode:
multi[0][3] = '3' multi[1][7] = 'h' multi[4][0] = 'J'
Een voorbeeldprogramma:
#include <stdio.h> int main(void) { int i, j; int groot[8][8], breed[25][12]; for (i = 0 ; i < 8 ; i++) for (j = 0 ; j < 8 ; j++) groot[i][j] = i * j; /* een vermenigvuldigingstafel */ for (i = 0 ; i < 25 ; i++) for (j = 0 ; j < 12 ; j++) breed[i][j] = i + j; /* een optellingstafel */ groot[2][6] = breed[24][10] * 22; /* lijn 1 (zie tekst) */ groot[2][2] = 5; groot[groot[2][2]][groot[2][2]] = 177; /* lijn 2 (zie tekst) */ for (i = 0 ; i < 8 ; i++) { for (j = 0 ; j < 8 ; j++) printf("%5d ", groot[i][j]); printf("\n"); /* nieuwe lijn voor elke verhoging van i */ }return 0; }
Dit programma werkt met dubbel-gedimensioneerde arrays. De variabele groot is een 8 bij 8 array dat in totaal 8 x 8 of 64 elementen bevat. Het eerste element is groot[0][0], het laatste is groot[7][7]. Een ander array genaamd breed is ook gedefinieerd, maar is niet vierkant, om aan te tonen dat dit niet verplicht is. Beide arrays worden gevuld met data, waarvan er één een vermenigvuldigingstafel voorstelt en waarvan de andere tot een optellingstafel gevormd wordt.
Om te illustreren dat individuele elementen naar behoefte aangepast kunnen worden, wordt één van de elementen van groot toegewezen aan de waarde van één van de elementen van breed, na vermenigvuldigd te zijn met 22 (lijn 1). Vervolgens wordt groot[2][2] toegewezen aan de willekeurige waarde 5 en deze waarde wordt gebruikt voor de subscripts van het toekenningsstatement op lijn 2. Lijn 2 is eigenlijk hetzelfde als groot[5][5] = 177; omdat elk van de subscripts de waarde 5 bevat. Dit wordt hier enkel gedaan om te illustreren dat elke legale expressie mag gebruikt worden in het subscript. Het moet aan twee zaken voldoen: het moet een integer zijn (alhoewel char ook toegelaten wordt) en de waarde moeten binnen het bereik van het subscript, waarvoor het gebruikt wordt, liggen. De volledige matrix groot wordt gedrukt in een vierkant, zodat u de waarden kan controleren.
opdrachten
1. Schrijf een programma met drie korte strings, van ongeveer 6 karakters elk en gebruik strcpy() om de string literals "een", "twee" en "drie" in die strings te kopiëren. Gebruik concatenatie om de drie strings in één grotere string van ongeveer 30 karakters te kopiëren en druk het resultaat tien maal op het scherm af. Vergeet de header string.h niet te includeren.
2. Definieer twee arrays van het type integer, array1 en array2 genaamd. Gebruik een loop om een boel waarden in elk van hen te stoppen en tel ze telkens op bij een ander array van tien elementen breed, genaamd array1_en_array2. Uiteindelijk druk je alle resultaten af in een tabel, via een index.
3. Vraag aan de gebruiker een voornaam, daarna een achternaam. Stop deze waarden in twee verschillende arrays, van het juiste type. Gebruik één of meerdere loops naar keuze om eerst de naam gewoon af te drukken en daarna de naam in omgekeerde volgorde af te drukken:
Wat is uw naam: Janssen Wat is uw voornaam: Jan U bent dus Jan Janssen. In omgekeerde volgorde is dat: nessnaJ naJ
voorbeelden
In dit hoofdstuk werden een paar volledige programma's getoond. Kopieer deze naar uw compiler en test de verschillende mogelijkheden. Bedenk dan zelf een paar toepassingen die gelijken op degene in dit hoofdstuk.
3-1.c: Een voornaam en achternaam concateneren met een spatie er tussen.
3-2.c: Een voorbeeld van strcpy().
3-3.c: Omzetten van kleine letters naar hoofdletters, met een do while(), een for() en een if() structuur.
referenties
[The C Library Reference Guide]
Eric Huss
[Pointers and Arrays Tutorial]
Ted Jensen