Weblessen.nl - Voor iedereen die wat wil leren..


C

Index
C Index
Voorwoord
De eerste stap
Een inleiding tot C
Programma besturing
Toekennen en Logisch vergelijken
Funkties en Variabelen
Defines en Macros
Strings en Tabellen
Pointers
Standaard Invoer / Uitvoer
Bestands Invoer / Uitvoer
Structuren en Unions
Dynamisch ngeheugen aanvragen
Karakter- en bitmanipulatie

Appendix
Hungarian Notation
Voorbeeldprogramma's
Totaal programma
Scherm- en bestands beschrijvingen
Aanpassingen voor VM/CMS

Structuren en Unions

 

Een beschrijving van de structuur

Een structuur is een door de gebruiker zelf gedefinieerd datatype. Je hebt als programmeur de mogelijkheid een datatype te definiëren dat aanmerkelijk complexer is als de datatypes die we tot nu toe in de cursus gebruikt hebben. Een structuur is opgebouwd uit variabelen van datatypes die eerder werden gedefinieerd. Dit mogen ook weer structuren zijn. Anders gezegd, een structuur is een groepering van aan elkaar gerelateerde gegevens. Structuren kunnen het leven van de programmeur bijzonder veraangenamen. Laten we eens naar een voorbeeld gaan kijken. Bestudeer daarom het programma STRUCT1.C. main () { struct _field { int sRow; /* field row */ int sColumn; /* field column */ int sLength; /* field length */ } veld1, veld2; veld1.sRow = 6; veld1.sColumn = 12; veld1.sLength = 20; veld2.sLength = 32; veld2.sColumn = 12; veld2.sRow = veld1.sRow + 2; printf ("veld1 staat op rij %d, kolom %d en is %d tekens lang\n", veld1.sRow, veld1.sColumn, veld1.sLength); printf ("veld2 staat op rij %d, kolom %d en is %d tekens lang\n", veld2.sRow, veld2.sColumn, veld2.sLength); } Het programma begint met de definitie van een structuur. Het gereserveerde woord struct wordt gevolgd door een structuur naam en een aantal variabele declaraties tussen accolades, die de componenten van de structuur vormen. Na de sluitaccolade vindt je de namen van twee variabelen, veld1 en veld2. Zoals vastgelegd in de definitie van een structuur is veld1 nu een variabele bestaande uit drie componenten, sRow, sColumn en sLength. Elk van deze drie componenten zijn gerelateerd aan veld1 en kunnen gegevens opslaan afhankelijk van hun type. De variabele veld2 bevat ook drie componenten, die wel dezelfde naam hebben, maar verder volledig andere geheugenlokaties beslaan. In feite hebben we hier 6 integers gedeclareerd.

Laten we eens wat nauwkeuriger naar de variabele veld1 kijken. De drie componenten waaruit deze structuur is opgebouwd, zijn alle integers en kunnen in principe overal toegepast worden in een C programma waar een gewone integer gebruikt mag worden. Je kunt er dus mee rekenen, afdrukken, in- of uitvoeren, enz. Het probleem is nu hoe een component van een structuur (ook wel structuur member genoemd) aan te wijzen in een programma. We doen dit door beide namen te gebruiken, dat wil zeggen structuur naam plus component naam met een puntje ertussen. Dus veld1.sLength is de naam van de variabele die de lengte component van het eerste veld aanwijst. Het is niet toegestaan aan de component naam alleen te refereren, omdat de compiler dan niet kan bepalen om welke component het gaat. Het opnemen van de variabele sLength in een programma is dus zinloos en leidt tot een foutboodschap van de compiler.

Het programma kent vervolgens aan elk van de drie componenten van veld1 een waarde toe. De componenten van veld2 krijgen in omgekeerde volgorde een waarde, om aan te tonen dat de volgorde waarin structuur members een waarde krijgen niet relevant is.

Nu alle variabelen een waarde hebben, kunnen we er iets mee doen. Om het eerste voorbeeld eenvoudig te houden, werd alleen met de rij van het tweede veld een berekening uitgevoerd en worden de variabelen slechts gebruikt om af te drukken. Het printf() statement vertoont geen echte bijzonderheden. De samengestelde naam van de variabelen wordt gebruikt, omdat dit de enige geldige manier is om ze aan te wijzen.

Structuren zijn bijzonder handig om gegevens te structureren en maken het je makkelijker om programma's te schrijven en te begrijpen. Dit eerste voorbeeld is te eenvoudig om je de kracht ervan te laten zien. De volgende voorbeelden zullen je al wat meer inzicht geven en zijn een goede basis om het Totaal Programma te doorgronden.

Compileer en test het programma.


Een tabel van structuren

Bestudeer het programma STRUCT2.C. Dit programma bevat dezelfde structuur als het vorige, echter deze keer declareren we 8 velden in de vorm van een tabel. Dit programma bevat dus 8 maal 3 is 24 variabelen, die alle een integer waarde kunnen bevatten. Tevens wordt een lus variabele gedeclareerd, om via een iteratie de structuur te vullen. main () { int index; struct _field { int sRow; /* field row */ int sColumn; /* field column */ int sLength; /* field length */ } veld[8]; for (index = 0; index < 8; index++) { veld[index].sRow = index + 4; veld[index].sColumn = 12; veld[index].sLength = 20; } /* endfor */ veld[1].sLength = 32; /* verander veld 2 */ veld[3].sLength = 32; /* verander veld 4 */ veld[5].sLength = 32; /* verander veld 6 */ for (index = 0; index < 8; index++) { printf ("veld %d: rij %2d, kolom %2d, %2d tekens\n", index+1, veld[index].sRow, veld[index].sColumn, veld[index].sLength); } /* endfor */ } De for lus wordt 8 keer doorlopen en in elke stap van de iteratie wordt aan de drie componenten van een veld een waarde toegekend. Op deze manier kan de interne administratie van het Yahtzee spel georganiseerd worden. Hier wordt geadministreerd op welke regel en welke kolom van het scherm een veld begint en wat de maximale lengte is.

In de volgende drie statements wordt aan sommige lengte componenten een andere waarde gegeven. Dit is om te laten zien hoe dat moet. In C is het niet mogelijk een structuur via een toekenning te vullen vanuit een andere structuur. Sommige moderne compilers ondersteunen dit echter wel. Kijk daarom altijd in de documentatie van de compiler hoe met structuren kan worden omgegaan.

Tenslotte worden alle gegenereerde waarden middels een iteratie afgedrukt. Compileer en test dit programma.


Pointers en structuren

Bestudeer het programma STRUCT3.C voor een voorbeeld van een programma waarin structuren en pointers in combinatie worden toegepast. Ook dit programma lijkt op de vorige twee en is er een uitbreiding op. main () { int index; struct _field { int sRow; /* field row */ int sColumn; /* field column */ int sLength; /* field length */ } veld[8], *ptr; for (index = 0; index < 8; index++) { ptr = veld + index; ptr->sRow = index + 4; ptr->sColumn = 12; ptr->sLength = 20; } /* endfor */ veld[1].sLength = 32; /* verander veld 2 */ veld[3].sLength = 32; /* verander veld 4 */ veld[5].sLength = 32; /* verander veld 6 */ for (ptr = veld; ptr < veld+8; ptr++) printf ("veld %d: rij %2d, kolom %2d, %2d tekens\n", (ptr-veld)+1, ptr->sRow, ptr->sColumn, ptr->sLength); } Naast de bekende declaraties vinden we nu ook de declaratie van een pointer met de naam ptr, hetgeen een pointer is naar de structuur _field. Het is niet toegestaan deze pointer te gebruiken als wijzend naar een ander datatype. Daar is een duidelijke reden voor, zoals we aanstonds zullen zien.

In de iteratie wordt nu de pointer gebruikt om de component variabelen aan te wijzen. Omdat zowel veld als ptr pointers zijn naar dezelfde structuur, kunnen we de structuur members benaderen middels de pointer. De variabele veld is een constante, dus die kunnen we niet variëren, maar ptr is een pointer en kan dus andere waarden aannemen. Als we aan ptr de waarde toekennen van veld, zal het duidelijk zijn dat de pointer naar het eerste element van de tabel met velden wijst.

Als we de waarde 1 bij de pointer optellen, zal deze naar het tweede element van de tabel gaan wijzen. Dit heeft te maken met hoe C met pointers omgaat. De compiler weet dat de structuur uit drie integers bestaat. Wanneer dus 1 moet worden opgeteld bij de pointer, zal in werkelijkheid 3 maal de lengte van een integer worden opgeteld, waardoor de pointer precies naar het begin van het volgende element wijst. Dit is dus de reden dat een pointer niet gebruikt kan worden om naar een variabele van een ander type te wijzen.

In iedere slag van de iteratie zal de pointer dus naar het begin van een tabel element wijzen. We kunnen de pointer dus gebruiken om toegang te krijgen tot de gegevens die in de tabel zijn opgeslagen. Het refereren aan gegevens middels een pointer wordt zo vaak toegepast, dat de programmeertaal C er een apart symbool voor heeft. Met behulp van de -> operator kan een structuur member worden benaderd. De variabele veld.sLength kan dus benaderd worden met ptr->sLength, wanneer ptr naar veld wijst.

Omdat de pointer naar de structuur wijst, moeten de componenten dus individueel aangewezen worden. Het voorbeeld programma laat in de twee iteraties verschillende manieren zien hoe dat kan. In de eerste iteratie wordt de waarde van de pointer samengesteld uit het begin adres van de tabel plus een afstand. In de tweede iteratie wordt de pointer zelf gebruikt en telkens een element verschoven. De tweede iteratie laat ook nog eens duidelijk het rekenen met pointers zien. Bestudeer het programma aandachtig en met name de uitdrukking (ptr-veld)+1. Compileer en test het programma, bestudeer de uitvoer en kijk of je begrijpt wat er wordt afgedrukt.


Geneste structuren

Bestudeer het programma GENEST.C. Dit programma bevat een geneste structuur, dat wil zeggen een structuur met daarin een structuur. De structuren die we tot nu toe zagen waren enkelvoudig. Structuren mogen tot in oneindige hiërarchie worden opgebouwd. Het is een groot voordeel structuren uit deel structuren op te mogen bouwen. Het opbouwen van een complexe structuur in een keer zou tot een zeer onoverzichtelijk programma leiden. main () { int index; struct _field { int sRow; /* field row */ int sColumn; /* field column */ int sLength; /* field length */ }; struct _screen { int sRows; /* number of rows on screen */ int sColumns; /* number of cols on screen */ struct _field veld[8]; } scherm; scherm.sRows = 24; scherm.sColumns = 80; for (index = 0; index < 8; index++) { scherm.veld[index].sRow = index + 4; scherm.veld[index].sColumn = 12; scherm.veld[index].sLength = 20; } /* endfor */ printf ("Het scherm heeft %d regels van %d tekens\n\n", scherm.sRows, scherm.sColumns); for (index = 0; index < 8; index++) { printf ("veld %d: rij %2d, kolom %2d, %2d tekens\n", index+1, scherm.veld[index].sRow, scherm.veld[index].sColumn, scherm.veld[index].sLength); } /* endfor */ } De eerste structuur is weer onze veld structuur en bevat drie integers. De structuur heeft wel een naam, maar er zijn geen variabelen van dit structuur type gedeclareerd. We hebben hier dus te maken met de definitie van een structuur, er is dus geen geheugen ruimte gereserveerd. De naam _field kan worden gebruikt om aan de structuur te refereren. Eigenlijk hebben we hier een nieuw type gedefinieerd. Dit type kunnen we in verdere declaraties gebruiken, net als we dat deden met de typen int en char. Als we dat doen, zijn we verplicht het gereserveerde woord struct bij de declaratie er voor te zetten.

Bij de declaratie van de tweede structuur kunnen we dit zien. Eerst worden twee integers als componenten gedefinieerd, gevolgd door een variabele van het type struct _field. De variabele van dit structuur type heeft de naam veld en is tevens een tabel van 8 elementen. Omdat een veld weer uit 3 componenten bestaat, bevat de scherm structuur dus 2 plus 8 maal 3 is 26 componenten, alle integers. De variabele die bij deze structuur hoort, heeft de naam scherm.

Het programma kent eerst waarden toe aan de enkelvoudige componenten van de scherm structuur. Om deze te benaderen is het toevoegen van de structuur naam scherm voldoende. Vervolgens krijgen de integers van de tabel een waarde. Omdat dit een tabel is van structuren, deel uitmakend van een structuur, zijn drie namen nodig. Om bijvoorbeeld het lengte veld van het derde element te benaderen, is de variabele naam scherm.veld[2].sLength vereist.

Om te laten zien hoe structuur variabelen op een tweede en derde niveau van hierarchie worden benoemd in de printf() funktie, worden alle variabelen afgedrukt. Compileer en test dit programma nu.

In principe kun je doorgaan met het nesten van structuren totdat je er zelf van in de war raakt. Als je het op de juiste wijze doet zal de compiler er nooit van in de war raken, omdat er geen formeel gedefinieerde limiet bestaat. Wel is er een praktische limiet. Wanneer je meer dan drie niveaus diep gaat, zul je er al moeite mee hebben.

Structuren kunnen tabellen bevatten bestaande uit weer andere structuren die als zodanig ook weer uit tabellen of structuren bestaan. Ook recursieve structuren zijn mogelijk. De strekking van deze uitleg is dat je verstandig met structuren moet omgaan, zeker in het begin. Begin eerst voorzichtig. Krijg je wat meer ervaring, dan kun je met complexer structuren gaan werken. Houdt het altijd overzichtelijk.

Erg ingewikkelde structuren worden hier verder niet behandeld. Het Totaal Programma bevat wel een aantal mooie voorbeelden. Bij de bestudering daarvan zul je niet al te veel problemen meer ondervinden, wanneer je dit hoofdstuk hebt begrepen.


Een beschrijving van de union

Bestudeer het programma UNION1.C. Een union geeft je de mogelijkheid om gegevens in het computer geheugen als zijnde van verschillend datatype te beschouwen, of om dezelfde gegevens met verschillende namen te benaderen. main () { union { int waarde; struct { char eerste; char tweede; char derde; char vierde; } deel; } nummer; long index; for (index = 753; index < 0x0FFFFFFF; index *= 4) { nummer.waarde = index; printf ("%08X - %02X %02X %02X %02X\n", nummer.waarde, nummer.deel.eerste, nummer.deel.tweede, nummer.deel.derde, nummer.deel.vierde); } /* endfor */ } In het voorbeeld programma staat de declaratie van een union die uit vier onderdelen is opgebouwd. Allereerst is er de integer variabele met de naam waarde. Voor het gemak gaan we er even van uit dat deze is opgeslagen als 32-bits getal, 4 bytes. Dan is er een structuur variabele van 4 characters met de naam deel. Deze bevat de componenten eerste, tweede, derde en vierde. Deze vier character variabelen zijn 8 bits groot, 1 byte, en beslaan hetzelfde geheugengebied als de integer. Ze liggen als het ware over elkaar heen. Dat is nu precies wat een union doet. De union maakt het je mogelijk gegevens van verschillend type in dezelfde geheugen lokaties op te slaan. Tenslotte is er de union variabele nummer.

In het voorbeeld programma wordt een getal opgeslagen in de component waarde. Dit getal wordt hexadecimaal afgedrukt, waardoor goed de samenstelling van de bits te controleren valt. Vervolgens worden de 32 bits waaruit het getal bestaat in vieren geknipt en elk deel afzonderlijk afgedrukt middels een character variabele van 8 bits. Door het getal met 4 te vermenigvuldigen schuiven alle bits 3 op naar links. Compileer en test dit programma, zodat je kunt zien hoe dat in zijn werk gaat.

Het benaderen van een union member gaat op exact dezelfde wijze als het benaderen van een structuur member. Bestudeer het voorbeeld, zodat verdere uitleg daaromtrent niet nodig is.

Unions worden niet zo vaak toegepast en zeker niet door beginnend programmeurs. Je zult ze zo hier en daar wel eens tegenkomen. Dat is de reden dat er enkele aandacht aan wordt besteed. Je hoeft er niet alles van te weten op dit moment. Ook het Totaal Programma bevat geen unions. Besteed er dus niet te veel tijd aan. Structuren zijn veel belangrijker. Probeer die wel goed te begrijpen.


Een voorbeeld programma

Bestudeer nu het programma ZEGTYD.C. Dit programma bevat heel veel elementen van hetgeen je tot op heden hebt bestudeerd. Alle onderwerpen in een klein programma. De bedoeling van het programma is dat het de systeemtijd afdrukt als ware het gesproken tekst. Een conversie van numeriek naar uitgesproken tekst als het ware. Laten we er maar eens naar kijken. #include <stdio.h> /* standard Invoer/Uitvoer header file */ #include <time.h> /* datum/tijd funkties header file */ char wrd[15][10] = { "twaalf", "een", "twee", "drie", "vier", "vijf", "zes", "zeven", "acht", "negen", "tien", "elf", "twaalf", "dertien", "veertien" }; main () { int hh, mm; time_t tijd; struct tm *t; tijd = time (NULL); t = localtime (&tijd); hh = t->tm_hour; mm = t->tm_min; printf ("Het is "); if (mm < 1) printf ("precies %s uur ", wrd[hh%12]); else if (mm < 2) printf ("een minuut over %s ", wrd[hh%12]); else if (mm < 15) printf ("%s minuten over %s ", wrd[mm], wrd[hh%12]); else if (mm < 16) printf ("kwart over %s ", wrd[hh%12]); else if (mm < 29) printf ("%s minuten voor half %s ", wrd[30-mm], wrd[hh%12+1]); else if (mm < 30) printf ("een minuut voor half %s ", wrd[hh%12+1]); else if (mm < 31) printf ("half %s ", wrd[hh%12+1]); else if (mm < 32) printf ("een minuut over half %s ", wrd[hh%12+1]); else if (mm < 45) printf ("%s minuten over half %s ", wrd[mm-30], wrd[hh%12+1]); else if (mm < 46) printf ("kwart voor %s ", wrd[hh%12+1]); else if (mm < 59) printf ("%s minuten voor %s ", wrd[60-mm], wrd[hh%12+1]); else printf ("een minuut voor %s ", wrd[hh%12+1]); printf ("... \n"); } Het programma begint met twee toevoegingen. Het standaard Invoer/Uitvoer Bestand en een bestand met de definities van diverse tijd funkties en structuren.

Er wordt een tabel van strings gedeclareerd. In C is dat een tweedimensionale tabel van variabelen van het type char. De strings zijn de woorden van de uren. De subscript op de tabel komt overeen met het uur of de minuut; het element komt overeen met het uur of de minuut plus 1. Uur nul is uur twaalf is element 1 is subscript 0. Minuut veertien is element 15 is subscript 14.

Alle element zijn te lang, maar doordat strings eindigen bij het NULL karakter, zal toch alles correct worden afgedrukt.

In het hoofdprogramma volgt de declaratie van twee integer hulpvariabelen hh en mm, voor respectievelijk de uren en minuten. De variabele tijd is van het type time_t, een type dat vooraf gedefinieerd is in het time.h bestand. Tenslotte is er de variabele t, hetgeen een pointer is naar een variabele van het structuur type tm. Ook dit laatste type staat vooraf gedefinieerd is in het time.h bestand.

Middels funktie time() wordt de systeemtijd opgehaald en toegekend aan variabele tijd. Met deze variabele als parameter van de funktie localtime, krijgt de pointer zijn correcte waarde en zal dus naar een structuur wijzen waarvan de componenten de systeemtijd bevatten. Van deze componenten hebben we alleen uur en minuut nodig. De namen daarvan zijn hour en min.

De wijzerplaat wordt nu in een aantal specifieke delen geknipt en van elk deel de uitspraak bepaald. Een switch statement zou hier op zijn plaats zijn, echter het aantal case's wordt wat groot. Het voorbeeld programma toont een aardig alternatief hiervoor&colon. een boom van if/else combinaties.

Hoewel wrd een tweedimensionale tabel is van characters, wordt hier gebruik gemaakt van het feit dat de variabele wrd een eendimensionale tabel is van strings. Met behulp van de %s specificatie in de printf() parameter wordt een van toepassing zijnde tekst afgedrukt. Het programma sluit af met het afdrukken van drie puntjes en een NewLine karakter.

Compileer en test dit programma.



Webdesign

Maak van Weblessen.nl uw startpagina!
Plaats Weblessen.nl bij uw favorieten. Neem contact met me op.
Heb je een Hosting?
Geef hier jouw mening over jouw web hosting

Webadres.info: Goede domeinnaam kiezen

Gesponsorde links:
Budget Webhosting
Web2host.nl
10eurohost.nl
Denit Hosting Solutions
YourHosting.nl
Starthosting.nl
Eduvision.nl
Educruitment.nl
Webadres.info


De link top 5:
Gratis Computercursussen
WebmasterStartpagina
MijnStartpagina.nu
Bluebird Animatie
Anouksweb
Link aanmelden
Alle Partners

Webmasterwoordenboek
A | B | C | D | E | F
G
| H | I | J | K | L | M
N
| O | P | Q | R | S | T
U | V | W | X | Y | Z

Films vanavond op Tv:

De klok:

(advertentie)

HTML leren
PHP cursus
XML lessen
XHTML les
CSS leer
leer C
REXX online
Red Hat Linux cursus