|
Staf Wagemakers
Inleiding CInleiding CDit document is afgeleid van "Coronado Enterprises C TUTOR (ver 2.00) Nov 21, 1987" Inhoudsopgave
1. "Je eerste C programma."Een nieuwe programmeertaal leren is niet zo eenvoudig, men moet nieuwe instrukties en begrippen gebruiken, oude gewoontes afleren, ... De beste programmeertaal is immers deze welke men kent en gewoon is om te gebruiken! 1.1 Alle begin is moeilijk!Bij C moet men met de volgende zaken rekening houden. De manier om een programmeertaal te leren is te kijken naar een simpel programma dat in deze taal geschreven is. Onderstaand voorbeeld is zo'n eenvoudig programma ( het doet namelijk niets).
Het woord "main" is erg belangrijk in een C programma, het moet namelijk eenmaal voorkomen of beter het mag maar eenmaal voorkomen in een C programma. De ronde haakjes geven aan dat het om een procedures gaat, tussen deze haakjes kunnen we eventueel ook variabelen plaatsen (hierover later meer). Met het woordje "main" geven we aan waar een programma begint. De {} geven het begin en het einde van de procedure aan. 1.2 Een programma dat iets doet!Onderstaand programma is een meer intersant voorbeeld. Dit programmaatje zet "Dit is een test!" op het scherm. Met de instruktie printf kunnen we dus data op het scherm zetten. De data die aan een instruktie of een procedure doorgegeven wordt staat in C altijd tussen ronde haakjes, met de aanhalingstekens geven we aan dat het om tekst gaat welke rechtstreeks naar het scherm gestuurd moet worden. Met ";" geven we het einde van een instruktie of funktie aan, dit moet in C altijd gebeuren!
Het volgende programma zet wat meer tekst op het scherm, "\n" komt overeen met een return of m.a.w. ga naar de volgende lijn.
1.3 En cijfers?Het volgende voorbeeld zet de waarde van een variabele op het scherm.
Met "int getal" definiëren we een integer met de naam "getal". In C moeten alle variabelen eerste gedefinieerd worden. Met "%d" bepalen hoe de variabele "getal" op het scherm gezet wordt, hier decimaal. 1.4 Hoe plaatsen we kommentaar in een C programma?Kommentaar wordt vaak gebruikt in een programma het dient om de werking van een programma te verduidelijken, voor jezelf én iemand anders. Met "/*" geven we het begin van de kommentaar aan, met "*/" het einde.
1.5 Programmeer opdrachten!1. Schrijf een programma dat je naam, adres en telefoonnummer op het scherm zet. Voor de postkode en nummer gebruiken we variabelen. 2. Lussen en sprongen.In dit hoofdstuk bekijken we een belangrijk onderdeel namelijk "programma kontrole". Hiermee kunnen we bepaalde zaken een aantal keer uitvoeren, de werking van ons programma beïnvloeden enz. 2.1 De "while" lusOnderstaand programma demonstreert de werking van de while instruktie. Het programma begint met het definiëren van een integer met de naam "x", deze variabele wordt eerste op 0 gezet. Integenstelling tot andere programmeertalen (zoals bv. BASIC) heeft een nieuwe variabele in C een willekeurige waarde. De "while" lus wordt uitgevoerd totdat er niet meer aan de voorwaarde wordt voldaan, de instrukties die tot de "while" lus behoren worden gegroepeerd door de akkolades.
2.2 De "do ... while" lusEen variant van de "while" lus is de "do ... while" lus, het volgende voorbeeld illustreert deze. Dit programma is bijna identiek aan het vorige, alleen wordt de lus gestart met de "do" instruktie, de voorwaarde staat hier achteraan de lus namelijk de "while" instruktie. Dit heeft als gevolg dat de "do ... while" lus altijd éénmaal doorlopen wordt! Indien we dus x=0 vervangen door x=7 zal het programma de waarde van x eenmaal afprinten, ook al is er niet aan de voorwaarde voldaan!
2.3 De "for" lusDe "for" lus is eigelijk niets anders dan een verkorte notatie van de "while" lus, volgend voorbeeld illustreert de "for" lus. Net zoals bij de "while" lus geven we een variabele een bepaalde waarde, stellen we een voorwaarde en verhogen (of verlagen) deze variabele, alleen is de notatie véél korter.
2.4 De "if" en "else" instruktiesMet de "if" instruktie kunnen we een bepaalde voorwaarde stellen, hierdoor is een mogelijk de werking van een programma te beïnvloeden door bv. een variabele. Met de "else" instruktie kunnen we bepaalde delen van het programma laten uitvoeren die niet aan de "if" voorwaarde voldeden.
Let op de dubbele gelijk - aan tekens bij de "if" instruktie, C maakt een onderscheidt tussen een vergelijking (aangeduid met "=="), en een overdracht ( aangeduid met "="). Achter de "if" of "else" verwacht de C - kompiler slecht één instruktie, voor meerdere kommando's moeten we gebruik maken van de akkolades ( { <instr. 1< ; <instr. 2< ; ... ;} ). 2.5 break en continueMet break en continue kunnen we een procedure of een instruktie onderbreken en voorzetten.
In de eerste "for" lus, de "break" lus dus, zal de lus door if(x==8) break; onderbroken worden indien x gelijk is aan 8. Tijdens de tweede "for" lus, de "continue" lus dus, zal indien x=8, het programma verder gaan aan het einde "for" - lus. Of m.a.w. de waarde 8 zal niet afgeprint worden, maar de "for" lus loopt normaal door! 2.6 De "switch" instruktie.Met de "switch" instruktie kunnen net zoals bij de "if - else" instrukties de werking van ons programma beïnvloedden door een variabele. Dankzij de "switch" instruktie kunnen we echter lange en onoverzichtelijke "if - else" opeenvolgingen vermijden!
2.7 De "goto" instruktie.De "goto" instruktie is een omstreden instruktie, er zijn namelijk mensen die er boeken over geschreven hebben. Hierin (probeerde) ze duidelijk te maken dat de "goto" instruktie niet thuis hoorde in een "gestruktureerde" programmeertaal. De "goto" instruktie is echter een volwaardige instruktie in C en vele andere programmeertalen, bovendien zijn er situaties te bedenken waarin de "goto" instruktie de eenvoudigste (en dus de beste) oplossing is. Het zou dus stom zijn om dan géén "goto" te gebruiken!
2.8 Programmeer opdrachten!1. Schrijf een programma dat je naam 10 keer op het scherm zet. Maak dit programma 3 maal, elke keer met een andere lus metode. 2. Schrijf een programma dat van 1 tot 10 telt, print elk getal op het scherm. Bij 3 én 7 toon je een ekstra berichtje op het scherm. 3. Datatypes, bewerkingen & vergelijkingenIn de twee vorige hoofdstukken hebben al een datatype leren kennen nl. de integer, in dit hoofdstuk gaan we meer datatypes bekijken. Ook gaan we dieper in op wiskundige bewerkingen en vergelijkingen, C heeft hiervoor ook verkorte notaties dit maak de bronkode vaak moeilijk leesbaar voor mensen die C niet zo goed kennen. 3.1 Eenvoudige bewerkingenIn het onderstaand voorbeeld zie we enkele eenvoudige bewerkingen nl.: +(optelling), -(aftrekking), *(vermenigvuldiging), /(deling), %(rest).
3.2 Andere datatypes
In het vorige voorbeeld leren we een aantal nieuwe datatypes kennen (int kenden we al).
Laten toch nog even dieper ingaan op de werking van het vorige programma. Zoals hierboven besproken is een "char" in wezen een "int" die door 1 byte voorgesteld wordt, we kunnen dus zonder problemen een "char" omzetten in een "int". In de andere richting (van "int" naar char" dus) is er geen standaard, indien de integer buiten het bereik van de "char" valt zal deze een waarde krijgen tussen de -127 en de +128. Zetten we een integer om in een "float" zal deze laatste de getalwaarde van de integer aannemen. In andere richting (van "float" naar "int"), zal de C - kompiler de cijfers na de komma laten vallen. 3.3 Uitgebreide datatypesC heeft nog variaties op degene die we al besproken hebben. Zo heeft een "int" de volgende uitbreidingen:
Enkel de "long", "short" en "unsigned" moet men eigelijk in een programma schrijven, de "int" zal door ervaren programmeurs weggelaten worden. Het "float" datatype heeft ook een uitbreiding nl. de "double" en heeft een bereik van 1.7E-308 tot 1.7E+308, het aantal cijfers na de komma verschilt soms van C kompiler tot C kompiler. Er zijn nog andere samenstellingen mogelijk zoals de "long unsigned int", "unsigned char", enz. 3.4 Konversie karaktersWe hebben al een konversie karakter van "printf" bekeken nl. "%d", met "printf" kunnen echter nog andere datatypes weergeven.
Andere konversie karakters zijn:
Ook is het mogelijk om met "printf" de uitvoer op een bepaalde manier te schikken, om bv. waarde in kolommen op het scherm te krijgen. In het voorgaande programma zien we ook hier een voorbeeld van. Met "%7d" bv. wordt alles rechts uitgelijnd met een veldbreedte van zeven, met "%-7d" links uitgelijnd met een veldbreedte van zeven. Algemeen:
3.5 Eenvoudige vergelijkingenHet volgende programma illustreert enkele eenvoudige vergelijkingen, in de kommentaar wordt het resultaat vermeld. Misschien nieuw is de logische "not" en inverteert het resultaat van een vergelijking of een andere bewerking, zo is != niet gelijk aan.
Groter dan of gelijk, kleiner dan, ... vergelijkingen bestaan natuurlijk ook in C, maar worden in dit voorbeeld niet aangehaald. 3.6 waar of nietwaar?????Het is misschien niet slecht om eens te bekijken wat waar ("true") en nietwaar ("false") voor C is. C bekijkt nietwaar als gelijk aan nul, en waar als groter dan nul of m.a.w. positief, de meeste C kompilers nemen voor waar "1" aan maar het is niet aan te raden deze "1" te gebruiken in een wiskundige bewerking.
In de eerste vergelijking is het resultaat waar, x wordt dus positief en z 111. De tweede vergelijking is geen vergelijking, de getalwaarde van "y" wordt overgedragen naar "x" vermits deze positief is wordt dit door de C kompiler als waar bekeken. In de derde vergelijking wordt dit verduidelijkt vermits "x" nul wordt, wordt deze als nietwaar bekeken. De vierde vergelijking is eigelijk analoog de getalwaarde van "y" (3 dus ) wordt overgedragen op "x" het resultaat is dus waar ongeacht de vorige waarde van "x". De laatste vergelijk is vrij duidelijk vermits "x" gelijk is aan nul wordt deze als nietwaar bekeken. 3.7 "and" en "or" vergelijkingen.Een "and" - vergelijking wordt in C voorgesteld als "&&", en een "or" - vergelijking als "||", het volgende voorbeeld illustreert "and" -en "or" vergelijkingen.
De eerste is vrij duidelijk de twee vergelijkingen zijn waar, het resultaat is ook waar. De tweede (een "or" vergelijking) is ook waar vermits z>12. Nummer vier is een goed voorbeeld van een "and" vergelijking vermits zowel "x", "y" en "z" groter zijn dan nul (waar dus) is de vergelijking ook waar. De vijfde is analoog, vermits "x" niet gelijk is aan 2, is deze nietwaar. 3.8 Pas op!!!De volgende vergelijkingen zijn veel gemaakte fouten.
3.9 Verkorte notaties.Verkorte notaties zijn zeer populair in C, toch zijn veel mensen er geen voorstanders en gebruiken deze dan ook niet. Het programma wordt op deze manier héél wat minder duidelijk voor andere (zeker voor beginnende programmeurs), toch is het belangrijk deze te begrijpen om de bronkode van iemand anders te kunnen lezen. Bovendien bespaard het veel wat schrijfwerk. 3.9.1 Verkorte bewerkingen.
Vorig programma demonstreert enkele verkorte notaties van bewerkingen, met x++ en ++x doe we hetzelfde als x=x=+1. Toch bestaat er een verschil tussen deze twee, dit wordt in de twee volgende bewerkingen duidelijk.
In het bovenstaand programma hebben nog enkele voorbeelden van verkorte bewerkingen, hierbij wordt een variabele met zichzelf bewerkt door een vast getal of een andere variabele. 3.9.2 Verkorte vergelijkingen.Ook voor vergelijkingen bestaan er verkorte notaties het volgende programma is hier een voorbeeld van.
De eerste vergelijking is de "normale" uitdrukking, de tweede is de verkorte schrijfwijze van de eerste. Indien de voorwaarde (b>=3) waar is wordt a 2, indien de voorwaarde nietwaar is wordt a gelijk 10. De twee volgende vergelijkingen zijn analoog aan de vorige. 3.10 Samenvatting3.10.1 Datatypes
3.10.2 Vergelijkingen
3.10.3 BitmanipulatiesIn dit hoofdstuk hebben geen bit manipulaties besproken in de onderstaande tabel staan de meeste gebruikte bewerkingen op bit niveau vermeld.
3.11 Programmeer opdrachten!1.Schrijf een programma dat van 1 tot 12 telt en dat telkens ook het kwadraat uitrekent.
2.Schrijf een programma dat van 1 tot 12 telt en dat telkens ook 1/x uitrekent tot 5 cijfers na de komma.
3. Schrijf een programma dat van 1 tot 100 telt, maar enkel de waardes tussen 32 en 39 op het scherm print. 4. funktiesTot nu hebben we enkel funkties gebruikt welke al in C zelf voorhanden waren zoals printf(), het is ook mogelijk om zelf funkties aan te maken. Deze maken om veel gebruikte handelingen te groeperen, bovendien wordt het programma er beter leesbaar door. 4.1 Lokale & globale variabelenHet volgende programma voorbeeld van programma met funkties.
Het programma begin met het definiëren van een integer met de naam som, vermits deze niet in een funktie gedefinieerd is spreken we van een globale variabele. Een globale variabele kan in alle funkties gebruikt worden en is voor al deze funkties ook hetzelfde. Variabelen welke in een funktie gedefinieerd zijn lokale variabelen, en kunnen enkel in de funktie gebruikt worden waarin ze gedefinieerd zijn. De funktie "kwadraat" is een voorbeeld hoe we een integer aan een funkties kunnen doorgeven, door kwadraat(index)wordt de getalwaarde van "index" overdragen aan "getal". Vermits "getal" in "kwadraat" gedefinieerd is kunnen we deze getalwaarde in de funktie kwadraat gebruiken. De werking van dit programma is vrij eenvoudig, in de "for" lus doorloopt index de getalwaarden van 1 tot 7, deze worden overdragen aan de funktie kwadraat welke (hoe kan het anders) het kwadraat uitrekent en afprint. In kwadraat wordt ook de som van al de kwadraten berekend, vermits som een globale variabele is, kan deze in heel het programma gebruikt worden. Met de funktie einde() printen wij de som van al de kwadraten af. 4.2 Waarden doorgeven zonder globale variabelen te gebruiken.In het voorgaande voorbeeld maakten we gebruik van de globale variabele "som" om een getalwaarde aan het hoofdprogramma terug door te geven. Dit kan ook anders het volgende programma is hier een voorbeeld van.
Door de in de funktie "kwadraat", de instruktie return(a) toe te voegen kan de waarde van "a" in het hoofdprogramma gebruikt worden. In de eerste "for" lus wordt deze waarde overgedragen aan "y", in de tweede wordt ze direkt gebruikt bij het afprinten. Nota: In tegenstelling tot andere programmeertalen zoals bv. Pascal maakt C geen onderscheidt tussen "funkties" en "procedures". In Pascal is een funktie een stukje programma wat een getalwaarde uitrekent. Voor C zijn funkties en procedures hetzelfde, beide termen worden dan ook door elkaar gebruikt! 4.3 funkties, welke geen integer teruggeven.C gaat er altijd vanuit dat een funktie een "int" terug geeft, indien we andere datatypes gebruiken dienen we dit aan de C - kompiler "te vertellen".
Het vorige programma is een vb. van funktie welke een "float" terug geeft. Het begint met het definiëren van een globale variabele "z", welke we later zullen gebruiken. Daarna wordt in het hoofdprogramma "main" een integer gedefinieerd, gevolgd door 2 "floats". Hierna komen 2 vreemde "floats", "sqr()" en "glsqr()" lijken op funkties, wat ze ook zijn. Dit is de manier waarop we C duidelijk maken dat in het de procedure "main" een funktie een variabele terug geeft welke geen integer is, in dit voorbeeld een float. In het midden van de listing staat de funktie "sqr(inval)", welke voorafgegaan wordt door het woordje "float", hiermee maken we duidelijk dat deze een variabele teruggeeft welke geen integer is. Dit lijkt misschien een beetje vreemd. Waarom moeten twee keer aangeven dat de funktie een "float" gaat teruggeven???? In C kunnen we echter gebruik maken van externe funkties, welke niet in één keer gekompileerd worden, in zo'n geval kan C niet weten welke datatype een externe funktie aan het hoofdprogramma gaat weergeven! 4.4 Variabelen en funkties
Het vorige programma is een voorbeeld van variabele gebruik in funkties, tot nu hebben variabelen altijd in het begin van het programma of een funkties gedefinieerd. Dit is echter geen verplichting! Als we het voorbeeld programma bekijken zien eerst 4 "rare" regels, negeer deze voorlopig hierop zullen we later terug komen. Hierna beginnen we met het definiëren van een globale variabele "teller", vermits deze buiten een procedure definieert is zal deze in al de volgende procedures bruikbaar zijn. Dit is ook zo voor "teller2" welke in het midden van het programma gedefinieerd is. De variabele "teller2" is echter niet bruikbaar in ons hoofdprogramma "main", omdat ze na deze procedure is aangemaakt. 4.5 "automatische" variabelenKijken we terug naar het hoofdprogramma dan zien we dat er een integer wordt gedefinieerd "index", voor "int index" staat ook nog het woordje "register" hierop zullen later terug komen. Deze variabele ("index") is enkel in het hoofdprogramma "main" bruikbaar vermits deze in de funktie gedefinieerd is. Dit noemt men ook soms een "automatische" (automatic voor de engelstaligen) variabelen, dit betekend dat de variabele enkel bruikbaar is in de funktie waarin ze is aangemaakt. En ander voorbeeld van een automatische variabele in de integer "a" in de "for" lus, vermits ze in de akkolades is aangemaakt is ze enkel bruikbaar in deze lus. Dit verklaart ook de term automatische variabelen, ze worden automatisch aangemaakt en weer vrijgegeven. 4.6 "void" funktiesKijken we de subprocedures "sub1", "sub2" en "sub3" dan zien we dat deze worden voorafgegaan door het woordje "void", dit zelfde woordje staat ook tussen de ronde haakjes. Door "void" tussen de ronde haakjes te plaatsen maken we C duidelijk dat we aan deze funktie geen variabele willen doorgeven. Met het woordje "void" voor de funktie maken we C duidelijk dat deze geen waarde kan teruggeven. 4.7 Dezelfde variabele teruggebruikenIn de funktie "sub1" wordt dezelfde variabele naam "index" gebruikt als in het hoofdprogramma, vermits in beide funkties "index" een lokale variabele is er probleem. Beide variabele hebben wel dezelfde naam maar hebben een aparte plaatst in het geheugen van de komputer. Hetzelfde gebeurt in de funktie "sub2" hier wordt dezelfde variabele naam gebruikt als de globale variabele "teller". Door deze naam opnieuw te gebruiken als een lokale in de funktie "sub2" is globale variabele "teller" niet beschikbaar in "sub2". 4.8 Wat is een "register" variabele?Zoals de meeste onder jullie weten bevat een microprocessor registers, welke veel sneller zijn het geheugen. Door het woord "register" voor de variabele definitie te plaatsen geven we C aan dat we een register van de µP willen gebruiken i.p.v. het geheugen. Het aantal registers is wel beperkt. 4.9 Wat is een "prototype"?In de regels 2 tot 4 maken we een prototypes aan voor de procedures "sub1", "sub2" en "sub3". Hiermee geven we aan hoe deze funkties er gaan uitzien, tijdens het kompileren zal C kijken of we tegen deze voorwaarden geen fouten maken indien dit wel het geval is zal C dit melden met een foutmelding. Of m.a.w. hiermee beveiligen we onszelf tegen programmeer fouten! 4.10 "static" variabelenAutomatische variabele zijn enkel gekend in de funktie waarin ze aangemaakt zijn, ze worden aangemaakt in het geheugen bij het de deklaratie en weer vrijgegeven bij het beëindigen van de funktie. Hierdoor gaat echter ook de waarde verloren!! Bij "static" variabelen, wordt een vaste geheugenplaats gereserveerd, de variabele in enkel bruikbaar binnen de funktie maar wordt niet uit het geheugen gewist, of m.a.w. de waarde blijft behouden. Het volgende voorbeeld zal dit ver duidelijken.
4.11 Wat is "rekursie"??Een rekursieve funktie is een funktie dit zichzelf aanroept het volgende programma is hier een voorbeeld van.
4.12 Programmeer opdrachten!1. Schrijf een programma dat je naam tien keer op scherm print, het printen gebeurt in een funktie. 5. Defines & MacrosDefines en macros zijn eigenlijk een hulp voor de programmeur, door van deze technieken gebruik te maken worden programma's duidelijker en overzichtelijker. 5.1 Wat is een "define"?Met een define kunnen bv. een waardegetal en naam geven, hierdoor kan een programma duidelijker worden. Bovendien hoeven we niet heel het programma te doorlopen indien er een bepaalde waarde verandert moet worden. Een eenvoudig voorbeeldje ...
Door de instruktie #define START 0 komt het woordje "START" overeen met de getalwaarde 0, met #define EINDE 9 komt "EINDE" overeen met 9. Deze twee worden later gebruikt om het begin en het einde in de "for"-lus aan te geven. 5.2 Wat is een "macro"?Een macro is analoog met een gewone "define", alleen bestaat een macro uit één of meedere instrukties. In het voorgaande voorbeeldje staan 2 macro's nl. "MAX" en "MIN", welke gebruikt worden om het respektievelijk het maksimum en het minimum te berekenen. Toch bestaat er een belangrijk verschil tussen een procedure (of een funktie) en een macro, bij een macro zal de C - kompiler telkens als hij dezelfde macro tegenkomt vervangen door de instrukties van deze macro. Bij een funktie wordt ernaar het begint van de funktie gesprongen, of m.a.w. een funktie staat slecht één keer in het geheugen, een macro het aantal keer dat ze wordt gebruikt! Het gebruik van lange veel gebruikte macro is dus zeker niet aan te raden (hierbij wordt er veel geheugen verspilt). 5.2.1 Kijk uit!Alhoewel macro's het leven van de programmeur moet vereenvoudigen zijn ze ook soms de reden van programatiefouten, het volgende voorbeeld bevat enkele fouten.
De eerste macro ("WRONG") is een voorbeeld van een foute derde machtsverheffing, in sommige gevallen zal ze werken in andere niet. De tweede ("CUBE") toont hoe het wel moet. Vervangen we "A" door bv. de eerste bewerking welke de macro moet uitvoeren dan krijgen we (1+5)*(1+5)*(1+5) = 216 voor de juiste ("CUBE"), voor de foute macro ("WRONG") krijgen we 1+5*1+5*1+5 = 16 wat een foutief antwoord is. De volgende macro ("SQUR") berekend het kwadraat, en is analoog aan de vorige macro's. De volgende macro ("ADD_WRONG") is een vb. van een foutieve optelling, dit kunnen we opnieuw het beste aantonen door getalwaarden in te vullen, 5 * (1) + (1) = 6, terwijl we eigelijk 10 als resultaat verwachten. De macro "ADD_RIGHT" zal wel de juiste uitkomst geven, 5*(1+1) = 10. 5.3 Wat doet "enum" instruktie?
Met de "enum" instruktie in derde regel van het vb. maken we een integer aan met de naam resultaat, de namen tussen de akkolades stellen voor deze integer een getalwaarde voor van 0 tot 6, hetzelfde gebeurt met de integer dag. De "enum" kan bv. handig zijn om te gebruiken in kombinatie met de "switch" instruktie om zo een programma overzichtelijker te maken. 5.4 Programmeeropdrachten!1.Schrijf een programma dat van 7 tot -5 telt, gebruik de "#define" instruktie om het begin en het einde aan te geven. 6. Strings & ArraysC maakt geen onderscheid tussen strings en arrays (in tegenstelling tot bv. BASIC), voor C is een string een verzameling van karakters. Wel zijn er bepaalde instrukties die het werken met strings eenvoudiger (zouden) maken. 6.1 Wat is array?Een array kunnen we het best omschrijven als een verzameling van data, welke van hetzelfde type zijn bv. integers, "floats", ... Een array van het "char" type noemen we een string. Het werken met arrays kan soms vrij verwarrend zijn, vooral bij meergedimensioneerde. 6.2 Wat is een string?Zoals eerder aangehaald is een string een verzameling van karakters, meestal gebruikt om tekst weer te geven bv. het beeldscherm. In C wordt (zoals in vele andere programmeertalen) een string afgelosten door de ASCII waarde nul, een voorbeeldje ...
Met char naam[5]; maken we een array aan van karakters welke 5 groot is met de naam "naam". Of m.a.w. "naam" is een string welke 5 karakters kan bevatten. Met de volgende programmaregels kopiëren we de woord "DAVE" in de string. Met het "printf" - kontrole karakter %s kunnen we een string afprinten, ook is het mogelijk om één karakter van de string af te printen zoals in het voorbeeld geïllustreerd. De laatste "printf" toont een deel van de string, hiervoor wordt gebruikt gemaakt van het "&" teken, hiermee bedoelen we een adres. Dit is eigenlijk een inleiding tot het volgende hoofdstuk en zullen we ook dan behandelen, nog even geduld ... 6.3 Enkele "string" instruktiesIn het vorig voorbeeld hebben we gezien hoe we data in een string kunnen plaatsen, dit was echter vrij omslachtig. C heeft echter instrukties die dit eenvoudiger maken, een voorbeeldje ...
De volgende "string" instruktie worden in het vb. gebruikt:
6.4 Een array van integersHet volgende programma is een voorbeeld van een array met integers, eerst wordt er een array aangemaakt met de naam "a" daarna een integer "x". In de eerste "for" lus wordt in de array data geladen, in de tweede wordt de array "a" achteruit apgeprint.
6.5 Een array van floatsHet volgende programma illustreert de array's met "floats".
De eerste regel van het programma demonstreert hoe op een eenvoudigere manier data in string kunnen plaatsen. Let op de legen haakjes bij de aanmaak van de string, de kompiler zal zelf voldoende plaats voorzien in het geheugen en de data in de string kopiëren inklusief het afsluitkarakter nul. In het programma wordt nog een string op deze wijze aangemaakt, let op de het woordje "static", dit vermijdt dat ze een automatische zou zijn. Voor de rest is een eigelijk niets nieuws in het voorbeeld. 6.6 Data terugkrijgen uit een funktieArray's gedragen zich in een funktie als de andere datatypes die we al kenden van de vorige hoofdstukken in een funktie. In het volgende voorbeeld wordt er een array van integers door gegeven aan de funktie "doe_maar". Zoals bij andere datatypes vertellen we de funktie door een variabele tussen de haakjes te plaatsen (hier "lijst"), hierna definiëren we dit het datatype van deze variabele. Let hierbij op de lege haakjes bij de definitie van de array "lijst", opnieuw zal C zelf voldoende plaats voorzien voor de array.
6.7 Multigedimensioneerde arraysIn het volgende voorbeeld worden meervoudige of multigedimensioneerde arrays gebruikt, dit wilt enkel zeggen dat de array uit meer dan één kolom bestaat. De arrays "m1" en "m2" zijn hier voorbeelden van, de rest van het programma is niet echt nieuw.
6.8 Programmeer opdrachten!1.Schrijf een programma met drie korte strings welke elk zes karakters groot zijn, kopieer hier met "strcpy" één, twee, drie in. Voeg de drie strings samen en print het resultaat tien keer op het scherm. 2.Defineer 2 integer arrays, van 10 integers lang. 7. "pointers"Een "pointer" is niets anders dan een adres, de meeste hogere programmeertalen schermen de programmeur bij het normale gebruik volledig af van deze adressen. C echter niet, dit komt in het begin misschien een beetje verwarrend over... 7.1 Een voorbeeldje
In bovenstaande voorbeeld worden "pointers" gebruikt, voor het ogenblik negeren we de aanmaak van de 2 variabelen met een sterretje. In de volgende lijn gebeurt niets nieuws, de integer "index" wordt gelijkgesteld met 39. De volgende regel is echter iets raar, "pt1" wordt gelijkgesteld aan "index" voorafgegaan door "&", hiermee bedoelen de adreswaarde van "index". Of nog anders gezegd, "pt1" is een "pointer" naar de variabele "index". Hierna wordt "pt2" gelijkgesteld aan "pt1", hierdoor is "pt2" ook een pointer naar "index". 7.2 Twee belangrijke regels.Een variabele voorafgegaan door een "&" is een adres naar deze variabele. Met pt1 = &index is de waarde van "pt1" gelijk aan het adres van "index". >2. Een "pointer" voorafgegaan door een sterretje komt overeen met de waarde van de variabele waarnaar de "pointer" wijst. Hierdoor zal het voorbeeld programma tijdens het afprinten 3 keer dezelfde waarde op het scherm zetten.
7.3 Er is maar één variabele!Het is belangrijk in te zien dat er in het vorige voorbeeld maar één variabele is nl. "index", "pt1" en "pt2" zijn pointers naar deze variabele. Dit wordt aangetoond met de regel *pt1 = 13; hierdoor wordt de variabele "index" veranderd. 7.4 Hoe maken we een pointer aan?In de derde regel van het programma maken we een variabele "index" aan, gevolgd door 2 pointers "pt1" en "pt2" dit maken we C duidelijk door een sterretje. Een pointer wijst altijd naar een bepaald type van een variabele, hierdoor kunnen de pointers "pt1" en "pt2" niet naar een andere variabele wijzen dan een integer. 7.5 Een string is eigenlijk een pointer.Aan de hand van het vorige voorbeeld hebben we heel wat teorie bekeken, belangrijke teorie, in C programma's worden pointers zeer vaak toegepast. Het is dus belangrijk om het werken met pointers goed te begrijpen. In het volgende voorbeeld bekijken hoe pointers toegepast kunnen worden op een string.
In het vorige hoofdstuk hebben we gezien dat een string een array is van karakters, we kunnen een string (en dus ook een array) echter ook bekijken als een pointer naar het begin van een array. Dit kunnen we begrijpen aan de hand van het voorbeeld. Eerst maken we een string aan met de naam "string", gevolg door een pointer naar een karakter ("daar") en twee karakters ("een" en "twee"). Hierna plaatsen we met de instruktie "strcpy" data in de string, het karakter "een" wordt gelijkgesteld aan de eerste karakter van de string. Vermits een string per definitie ook een pointer is doen we hetzelfde met "twee", "twee" neemt ook de waarde aan van het eerste karakter in de string. Het resultaat is dat beide karakters "een" en "twee" gelijk zijn aan 'D'. In het volgende deel van het programma gebeurt ongeveer hetzelfde, "een" wordt gelijkgesteld aan het negende karakter van de string (we beginnen vanaf 0 te tellen). Met "twee" gebeurt dit ook door gebruikt te maken van een string als een pointer. Het is fout om zaken te schrijven zoals "twee=*string[8];". Vermits een "daar" een pointer is kunnen we "daar" gelijk stellen aan een adreswaarde van een element uit een string. Dit wordt gedemonstreerd met daar = string + 11. Laten we toch opmerken dat vermits "daar" aangemaakt is een pointer naar een karakter, het enkel naar een karakter kan wijzen. Daar een element uit een string een karakter is kan "daar" ook hiernaar wijzen. 7.6 Data aan een funktie geven als een pointer.In het volgende voorbeeld geven data aan funktie door als een pointer.
We beginnen met 2 variabelen te definiëren "pindas" en "appelen", merk op dat beide normale variabelen zijn en geen pointers. We geven beiden een waarde en printen deze uit, roepen we de funktie "verander" aan. De variabele "pindas" wordt als een normale variabele aan de funktie door gegeven, de variabele "appelen" wordt als een adres doorgegeven. In de funktie "verander" geven dit aan door "noten" als een normale integer te definiëren en "fruit" als een pointer naar een integer. In de funktie "verander" printen we de twee waarden nogmaals uit, hierna veranderen we ze en printen de nieuwe waarden op het scherm. Dit zou vrij duidelijk moeten, vermits hier niets nieuws gebeurt. Bij het verlaten van de funktie gebeurt er echter iets raars, bij het opnieuw afdrukken van de twee variabelen in het hoofdprogramma zien we dat "pindas" terug haar oude waarde heeft. Dit komt omdat C een kopie maakt van de waarde en deze aan de funktie doorgeeft, en dus de originele intakt laat. De variabele "appelen" is echter wel verandert door de funktie, C heeft hier een kopie gemaakt van de adres waarde en deze aan de funktie doorgegeven. Hierdoor hebben we in de funktie een pointer naar de integer "appelen" en wordt deze wel aangepast. 7.7 Programmeer opdrachten!1. Maak een string aan kopieer hier data in met strcpy. 2. Pas nu het programma aan zodat de string achterstevoren op het scherm komt. 8. standaard invoer / uitvoer.Dit komt misschien een beetje raar over, maar de C taal heeft geen invoer / uitvoer instrukties, deze moeten in principe door de gebruiker (de programmeur dus) zelf aangemaakt worden. Toch zijn in de loop der jaren enkele libraries ontwikkeld welk een "facto" standaard zijn geworden. Hierbij moeten we echter wel opletten bij het overzetten we van onze programma's naar een ander platform of C - kompiler vermits we hier niet met een officiële te maken hebben ... 8.1 Include bestanden.In één van de vorige hoofdstukken hebben we al gebruik gemaakt van een zgn. "header" bestand nl. "stdio.h", hierin zijn enkele standaard funkties in opgenomen om invoer/uitvoer van data in programma eenvoudiger te maken. Door gebruik te maken van deze "header" bestanden, worden deze in het programma opgenomen, of het programma wordt hierdoor groter. Dit is dan ook een reden waarom deze instrukties niet standaard bestaan in C. Er bestaan nog andere "header" bestanden raadpleeg de dokumentatie van uw C kompiler voor meer informatie. Ook is het mogelijk om zelf "header" bestanden aan te maken om zo funkties aan te maken, welke in meerdere verschillende programma's bruikbaar zijn.We kunnen op twee manieren "header" bestanden in ons programma opnemen :
8.2 Een eerste voorbeeld.
In het bovenstaande voorbeeld zien we twee instrukties uit "stdio.h" nl. "getchar" en "putchar", zoals de namen dienen deze om een karakter te lezen ("getchar") en om het scherm te plaatsen ("putchar"). Indien we dit programma echter kompileren en uitvoeren zien echter iets raar. De funktie "getchar" wacht tot dat we de "return" toets indrukken het eerste getypte karakter wordt dan aan het programma doorgegeven. Via "putchar(c)" dit karakter dan nogmaals afgedrukt. Dit komt raar over omdat dit minder bruikbaar is in een programma. Gelukkig bestaan er andere instruktie om een karakter in te lezen. 8.3 De "scanf()" funktie.Met de "scanf()" instruktie kunnen we allerei data van het toetsenbord lezen zoals integers, "floats", strings, ... Het volgende programma lees een integer in.
Met scanf("%d",&x); lezen we een integer in, de data komt in x. Net zoals bij "printf" moeten we aangeven om welke type van data het gaat, we gebruiken hiervoor dezelfde konversie karakters. Let ook op het "&" voor x, hiermee geven we de adreswaarde door aan de funktie, "scanf()" verwacht altijd een pointer. Met de teorie uit het vorige hoofdstuk moet het duidelijk zijn waarom dit zo gebeurt. De rest van het programma zou vrij duidelijk moeten zijn, er telkens een integer ingelezen en afgedrukt totdat we het getal 100 ingeven. 8.4 Inlezen van een string.Met "scanf()" kunnen we ook een string inlezen het konversie karakters is hetzelfde als bij "printf", het volgende programma is hier een voorbeeld van. Let ook op het ontbreken van "&" bij de "scanf" instruktie, vermits tekst een array is en per definitie een al pointer is (zie vorige hoofdstuk). Verder zou de werking van het programma vrij duidelijk moeten zijn. Het programma vraag telkens een string en druk deze daarna af, totdat van een string beginnen met 'X'. Indien we dit programma kompileren en uitvoeren merken tot iets raars op, indien we in een string een spatie gebruiken wordt deze hierna weergegeven. Of beter gezegd, een spatie betekent voor "scanf()" einde string, ook dit komt vreemd over vermits dit minder bruikbaar is in programma. Er zijn echter ook libraries ontwikkeld welk invoer / uitvoer beter afhandelen dan "stdio.h" voorbeelden hiervan zijn "ncurses" uit de Unix wereld (ook overgebracht naar andere platformen), en "TurboVision" van Borland C++ ( enkel dos).
Er bestaat echter nog een andere manier om een string in te lezen, via de "fgets()" funktie welke eigelijk bedoel is om een string uit een bestand te lezen, hierover meer in het volgende hoofdstuk. Het onderstaande programma is hier een voorbeeld van. Met fgets(string,10,stdin)laden we een string van maksimaal 10 karakters in "a", we kunnen wel meer karakters intypen maar enkel de eerste 10 zullen in "a" ingeladen worden. Met "stdin" geven we "fgets()" aan dat we de data inladen van het "standard input device", het toetsenbord, i.p.v. uit een bestand.
8.5 Geheugen invoer / uitvoer.De volgende instrukties komen misschien raar over, en komen we ook niet in vele andere hoger programmeertalen tegen. In "stdio.h" bestaan er variaties van "printf()" en "scanf()" welke zaken in het geheugen kunnen schrijven of lezen, een voorbeeldje ...
De eerste nieuwe instruktie die we tegenkomen is "sprintf()", deze is identiek aan "printf()" enkel wordt de data hier niet op het scherm gezet, maar in een string in het geheugen geprint. In het voorbeeld wordt er data in "lijn" gekopieerd, deze data o.a. bevat de getalwaarden van de array "nummers". Hierna de string "lijn" via een normale "printf()" op het scherm gezet. De volgende nieuwe instruktie is "sscanf()" welke analoog is aan "scanf()" enkel werkt ook deze met string in het geheugen. In het voorbeeld wordt de data van array "nummers" op deze manier terug uit de string "lijn" gehaald en in de array "resultaat" geplaatst. Tenslotte drukken we via een "for" lus de array resultaat op het scherm. Deze technieken kunnen handig zijn om bv. een string in te lezen en daarna in het geheugen de konversie te doen. 8.6 foutmeldingen...Het kan in sommige toepassingen nodig zijn om zaken die op scherm komen naar een bestand te sturen. Dit kunnen we onder DOS of Unix eenvoudig doen door "prg(.exe) > test" aan de kommandolijn in te typen, hiermee sturen we data van "prg" naar het bestand test. Het kan echter ook nodig zijn dat de foutmeldingen op scherm blijven, en enkel de echte data naar het bestand worden gestuurd. Het volgende programma is hier een voorbeeld van. De instruktie "fprintf()" zal in het volgende hoofdstuk verder uitgelegd worden, zoals hier toegepast stuurt ze de data naar het "standard error device" of "stderr" het scherm dus. Runnen we het programma dan al de data op het scherm zetten, voeren we het uit met "prg(.exe) > test" dan zal de data die via "printf()" verzonden wordt in het bestand "test" komen, de foutmeldingen komen nog steeds op het scherm.
8.7 foutkodes...Foutkodes of "errorlevels" voor de engelstaligen, is een kode welke door een programma terug naar het besturingssysteem worden gezonden, deze zijn onder de meeste besturingssystemen bruikbaar. Dit gebeurt in vorige programma met exit(4), hiermee wordt het getal vier na het beëindigen van het programma aan het besturingssysteem doorgegeven. 9. Lezen & schrijven van bestanden.In "stdio.h" bestaan ook funkties om bestand aan te maken, te lezen en te schrijven. Eigenlijk is "stdio.h" hiervoor geschreven, dit verklaart ook waarom sommige funktie uit het vorige hoofdstuk niet echt deden werken zoals we zouden verwachten. 9.1 Openen van een bestand.Het openen van een bestand kunnen we doen door gebruik te maken van de funktie "fopen()", welke er als volgt uit ziet: fp = fopen("<Bestandsnaam>","operatie") Met "fp" wordt een "filepointer" bedoeld, hiervoor is in "stdio.h" een definitie nl. "FILE", we kunnen een "filepointer" of bestandswijzer eenvoudig aanmaken door bv. "FILE *bestand;". Met <Bestandsnaam>bedoelen we de naam van het te openen bestand ( dat had je wel door zeker ;-) ), hiervoor kunnen we eventueel ook van een string gebruik maken. Met "operatie" geven we aan hoe het bestand gaan openen, de volgende mogelijkheden kunnen we hiervoor gebruiken:
9.2 Sluiten van een bestand.Elk bestand dat via de funktie "fopen()" geopend is, moet ook voor het programmaeinde gesloten. Enkel waarmee het bestand gesloten is zijn we zeker dat de veranderingen aan het bestand ook daadwerkelijk zijn aangebracht. De funktie van "stdio.h" hiervoor is fclose(fp), waar "fp" de bestandswijzer is naar het te sluiten bestand. 9.3 Schrijven naar een bestand.Het volgende programma is een voorbeeld voor de aanmaak van een nieuw bestand. Met FILE *fp; maken we een "filepointer" of bestandswijzer aan met de naam "fp". Hierna maken een string "a" en een integer "x" aan. Met fp = fopen("10lijnen.txt","w"); maken we nieuw bestand aan met de naam "10lijnen.txt", bestond dit bestand al dan wordt het overschreven. Hier kopiëren we data in de string "a". De tegenhanger van "printf()" om dat naar een bestand te schrijven is fprintf (fp,"<DATA>",<VAR1>,<VAR2>"). In de "for" lus wordt data in het bestand geplaatst via deze instruktie.
9.4 Data bijvoegen aan een bestand.Het volgende voorbeeld voegt data bij aan het einde van een bestand. Hiervoor gebruiken we "a" in de "fopen()" funktie, om data aan het bestand bij te voegen gebruiken we nu de funktie "putc()" uit "stdio.h". Deze is de tegenhanger van "putchar()", of beter het is eigenlijk dezelfde funktie, "putchar(<char>)" is hetzelfde als "putc(<char>,stdin)". Deze techniek kunnen we voor al de funkties voor bestanden gebruiken, willen lezen van het toetsenbord (de standaard invoer) gebruiken "stdin" als "filepointer". Willen naar het scherm schrijven gebruiken we "stdout" als "filepointer". Kijken we terug naar ons programma dan zien we dat er voor de rest niets bijzonder gebeurt, misschien er op wijzen hoe de string naar het bestand wordt geschreven. In de tweede "for" lus wordt a[x]; als voorwaarde gebruikt, een string wordt afgesloten door een "0" dus deze wordt nietwaar bij het einde van de string.
9.5 Lezen uit een bestand.Het volgende programma lees uit een bestand hiervoor gebruiken "r" in de funktie "fopen()". In het programma wordt getest of te lezen bestand wel degelijk bestaat met if (fp ==NULL), "NULL" is voor ons aangemaakt in "stdio.h". Met de funktie "getc(fp)" halen we een karakter op uit het bestand. In de "do ... while" lus wordt er telkens een karakter opgehaald uit het bestand en op het scherm geplaatst totdat "c" gelijk is aan "EOF". Met "EOF" ( "End Of File") wordt het einde van het bestand bedoelt. Misschien opmerken dat er in het programma één karakter te veel wordt afdrukt, het "EOF" karakter wordt immers mee afgedrukt.
9.6 Pas op!Het gebruik van "EOF" geeft problemen met bv. "unsigned char" als karakter te gebruiken. Dit komt omdat "EOF" overeenkomt met "-1", en zal dus bij een "unsigned char" vertaalt worden als een 255. Of met andere worden er zal nooit een bestandseinde gevonden worden! 9.7 Lezen van een woord.De tegenhanger van "scanf()" is "fscanf()", in het vorige hoofdstuk hebben we gezien dat "scanf()" stop met lezen bij een spatie. Dit is ook zo bij "fscanf()", dit kan zijn zin hebben bij het lezen uit een bestand waar de data gescheiden is door een spatie. Het volgende programma is bijna identiek aan het vorige enkel wordt de data nu per woord op gehaald en afgedrukt.
Ook hier drukken we één woord te veel af vermits we eerst een woord ophalen, afdrukken en daarna pas testen op een "EOF". Dit kunnen eenvoudig oplossen door een "if" regel bij toevoegen, ook is het mogelijk door gebruikt te maken van een "while" lus. In het volgende programma is dit probleem opgelost met een "if".
9.8 De funktie "fgets(string,aantal,fp)"De funktie "fgets()" hebben in het vorige hoofdstuk al gebruikt om een string inlezen van het toetsenbord. We kunnen ook gebruiken om dat uit een string te lezen, net zoals bij het lezen van het toetsen stop "fgets()" met lezen bij een "return". Indien deze funktie het einde van het bestand heeft bereikt geeft het de waarde "NULL" door. Voor de rest gebeurt er eigenlijk niets nieuws in het volgende programma.
9.9 Een string gebruiken als bestandsnaam.Tot nu hebben in fopen altijd de bestandsnaam aan "fopen()" doorgeven tussen aanhalingstekens, we kunnen hiervoor ook een string gebruiken. Het volgende programma is identiek aan het vorige, enkel wordt er via "scanf()" een string lezen, welke we gebruiken als bestandsnaam in "fopen()".
9.10 Aansturen van de printer.Met de besproken funktie kunnen we ook de printer aansturen, vermits onder dos "PRN" als een bestand wordt bekeken (voor Unix kunnen we bv. "/dev/lp1" gebruiken). Het volgende programma is hier een voorbeeld van.
9.11 Programmeer opdrachten!1.Schrijf een programma dat twee bestandsnamen vraagt, één om een bestand te lezen één om te schrijven. 2.Vraag een bestandsnaam. Lees het bestand lijnen per lijn, druk deze lijn samen met een lijnnummer op het scherm af. 10. "struct" & "union".In dit hoofdstuk gaan we 2 nieuwe datatypes bekijken nl. "structure" en "union", alhoewel nieuw misschien foutief uitgedrukt is. Ze zijn bedoeld om data van verschillende types welke bij elkaar horen te groeperen. 10.1 De "struct" instruktie
Het programma begint met een definiëren van een "struktuur". De instruktie "struct" wordt gevolgd met enkele eenvoudige variabelen tussen de "{ }", welke onderdelen zijn van de struktuur. Na de "{ }" staan er 2 variabelen nl. "jongen" en "meisje", dit zijn de namen van de aangemaakte struktuur. Of m.a.w. "jongen" is een variabele met drie elementen "init", "leedtijd" en "graad" welke enkel data kan bevatten van het gedefinieerde type. Dit is ook zo voor "meisje" welk dus ook drie variabelen bevat, in het totaal hebben we dus 6 variabelen aangemaakt. Verder zien we in het programma hoe we deze variabelen kunnen aanspreken, met "jongen.init" spreken we het variabele "init" van de struktuur "jongen" aan. Deze techniek maakt het programmeren dus heel wat eenvoudiger en het programma wordt er veel overzichtelijker door. 10.2 Een array van strukturen.
In het vorige programma maken we gebruik van array waar de element van deze array bestaan uit een struktuur. We definiëren zo'n array simpelweg door de "{ }" een array naam te plaatsen. Voor de rest gebeurt in het programma niet echt iets nieuws, misschien er toch even op wijzen dat het ook mogelijk is om twee strukturen aan elkaar gelijk te stellen. Dit wordt in het voorbeeld geïllustreerd met de regel kind[10]=kind[4]; hiermee worden alle elementen van "kind[10]" gelijk aan de element van kind[4]. 10.3 Het gebruik van "pointers" in strukturen.Het volgende programma is bijna identiek aan het vorige, enkel worden er bij sommige operaties gebruikt gemaakt van pointers.
Het eerste verschil gebeurt al bij de aanmaak van de struktuur, hier word er een "pointer" met de naam "point" aangemaakt, welke een "pointer" is naar de struktuur. Zoals in het hoofdstuk "pointers" kunnen "point" niet naar een ander datatype laten wijzen. In het hoofdstuk "strings & array's" hebben we ook gezien dat een array een verzameling is van pointers. We kunnen "point" dus zonder problemen gelijk stellen aan "kind" welke een "pointer" is naar het eerste variabele, hier een struktuur, uit de array "kind[..]". C weet hoeveel geheugen plaatsen het moet reserveren voor een struktuur. Tellen we bij "kind" bv. 1 bij dan zal C zich op de tweede struktuur zetten, in werkelijkheid wordt er bij "kind" dus de struktuurgrootte bijgeteld. 10.4 Strukturen zonder naam.
De eerste struktuur "persoon" in het bovenstaande voorbeeld wordt niet gevolgd door een variabele naam, we hebben dus enkel een struktuur aangemaakt. Het nut hiervan wordt duidelijk gemaakt in de volgende struktuur "data", hierin wordt de struktuur "persoon" gebruikt als variabele "beshr". Door dus een struktuur zonder een variabele naam aan te maken kunnen we deze gebruiken in heel het programma zoals een ander datatype (int, char, long ...). 10.5 Wat is een "union"??Het volgende programma is een voorbeeld van een "union". In dit voorbeeld bestaat de "union" uit twee delen, het eerste deel is een integer "waarde" welke in 2 bytes in het geheugen van de komputer bewaard wordt. Het tweede deel bevat twee karakter (welke elk 1 byte groot zijn) nl. "een" en "twee". Deze twee karakters worden op dezelfde geheugenplaats bewaard als de integer "waarde", dit komt door de instruktie "union". Veranderen we de integer "waarde" dan "een" het eerste deel van "waarde" bevatten en "twee" het tweede. Dit wordt in het programma aangetoond door de "for" lus.
10.6 Een ander voorbeeld ...Het volgende programma is een meer praktisch voorbeeld, het houdt een database(je) bij van verschillende types van voertuigen. We zullen dit voorbeeld doorlopen van begin tot einde ...
We beginnen met het aanmaken van een paar konstanten met "#define". Hierna definiëren we een struktuur "automobile" welke enkele data velden bevat, dit is vrij duidelijk vermits we hier niets nieuws doen, we definiëren hier enkel de struktuur en maken nog géén variabelen aan. 10.7 Een nieuwe instruktie "typedef".Hierna definiëren we data met een nieuwe instruktie nl. "typedef". Hiermee definiëren we een kompleet nieuw datatype welke gebruikt kan worden zoals bv. "int" of "char" gebruikt kan worden. Merk op dat de gedefinieerde struktuur geen naam heeft, op het einde waar normaal de variabele naam staat is nu ingenomen door "BOATDEF". We hebben nu een nieuw datatype "BOATDEF" welke gebruikt kan worden om een struktuur aan te maken waar we ook maar willen. Merk opnieuw op dat we hier géén variabelen aanmaken, maar enkel een nieuwe struktuur definiëren. Uiteindelijk definiëren we een grote struktuur hierbij maken we gebruikt van de datatypes welke we hiervoor gedefinieerd hadden. De struktuur is opgebouwd uit 5 delen, twee eenvoudige variabelen "voertuig" en "gewicht", gevolgd door een "union" en als laatste we andere eenvoudige variabelen "waarde" en "eigenaar". Laten we even bekijken hoe de "union" is opgebouwd. Deze is gemaakt uit vier delen, het eerste deel is de variabele "wagen" welke aangemaakt wordt aan de hand van de struktuur "automobile" welke we eerder gedefinieerd hadden. Het tweede deel is de variabele "boot" welke een struktuur is zoals we deze eerder al gedefinieerd hadden in "BOATDEF". Het derde deel is de variabele "vliegtuig" welke aangemaakt wordt door een struktuur. Het vierde en het laatste deel is de variabele "ship" welke ook van het type "BOATDEF" is. We hebben nu een struktuur welke gebruikt kan worden om elk van de vier verschillende types van data strukturen te bewaren. De grootte van welk record zal de grote zijn van de grootste "union". In dit geval is het eerste deel van de union het grootste vermits het bestaat uit 3 integers. Het eerste lid van union zal dus de werkelijke grootte bepalen. De resulteerde struktuur kan gebruikt worden om een van de vier datetypes te bewaren, maar het is aan de programmeur om bij te houden wat hij bewaard heeft. Hiervoor dient de variabele "voertuig" hiermee houden we bij welke type van voertuig (en dus ook het datatype) in de struktuur bewaard werd. De vier konstanten welke in het begin van het programma aangemaakt werden worden hiervoor gebruikt. In de volgende regels wordt aangetoond hoe de aangemaakte struktuur gebruikt kan worden. Aan enkele wordt een waarde toegekend, enkele worden afgedrukt als illustratie. Een "union" wordt niet al te vaak gebruikt, en bijna nooit door een beginnende programmeur. Je zult toch in sommige programma's tegen komen het is dus zeker de moeite om te weten wat een "union" is. Je moet zeker niet in het begin alle details te weten, besteed er dus niet té veel tijd aan. Het is beter deze zaken te nader te bekijken wanneer je ze nodig hebt. 10.8 Wat is een "bitveld"?Het volgende programma is een voorbeeld van een "bitveld" (bitfield voor de engelstaligen). In dit programma maken we en "union" aan bestaat uit een integer "index" en bijgevolg 4 bytes groot is. De "union" bevat nog een struktuur welke 3 leden bevat, deze struktuur wordt op dezelfde geheugenplaats bewaard als de integer "index". De variabele "x" is enkel 1 bit breed, "y" en "z" zijn er 2 breed. Vermits de struktuur op dezelfde geheugenplaats bewaard wordt is "x" de minst beduiden bit, "y" bevat devolgende 2 en "z" de laatste twee. Indien we "x", "y" en "z" elk één bit groot maken tellen we binair.
10.9 Programmeer Opdrachten.1. Definieer een struktuur welke een veld bevat voor een string om naam in te bewaren, een integer voor voeten en een voor armen.
Een mens heeft 2 voeten en 2 armen Een hond heeft 4 voeten en 2 armen Een stoel heeft 4 voeten en 0 armen Een tafel heeft 4 voeten en 0 armen ...
2. Herschrijf 1. maar maak nu gebruik van "pointers". 11. Dynamisch geheugenBij dynamisch geheugen gaan we in ons programma pas geheugen bezetten als we het nodig hebben. Tot nu hebben we altijd gebruikt gemaakt van statisch geheugen, we definieerde een variabele en deze nam geduurde heel het programma geheugen in beslag. Met dynamisch geheugen kunnen we enkel geheugen gebruiken indien we het nodig hebben, met deze techniek kunnen we dus geheugen besparen. Bovendien weten we niet altijd op voorhand hoeveel geheugen we gaan nodig hebben. 11.1 Een voorbeeld...Aan de hand van het volgende voorbeeld zullen we de belangrijkste instruktie en begrippen die met dynamisch geheugen te maken hebben bekijken.
We starten het programma met het definiëren van een struktuur dier, welke drie velden bevat. Merk op dat we geen enkele variabele aanmaken (en dus ook géén geheugen bezetten!), we definiëren enkel drie pointers. In het programma zullen alle variabelen die er gebruikt worden dynamisch worden aangemaakt. 11.2 Het dynamisch kreëren van variabelen.Na het definiëren van de pointers wordt er een variabele dynamisch aangemaakt, "pt1" zal naar een struktuur wijzen welke 3 variabelen bevat welk eerder in het programma gedefinieerd werden. Het hart van de aanmaak is de "malloc()" funktie, hiermee kunnen we geheugen bezetten. Met "malloc(n)" bezetten we een geheugendeel wat n bytes groot is in de "heap". 11.3 Wat is een "heap"?Elke kompiler heeft zijn beperkingen op hoe groot een uitvoerbaar bestand kan zijn, hoeveel variabelen er gebruikt kunnen worden, hoe groot de bronkode mag zijn, ... Een van deze beperkingen bij DOS C-kompilers is de limiet van 64Kb, bij C-kompilers van andere besturingssystemen zoals Unix, OS/2 of een kompiler onder DOS met een zgn. DOS Extender bestaat deze beperking niet. Een "heap" is gebied van geheugen buiten deze 64Kb limiet welke gebruikt kan worden in het programma om variabelen in te bewaren (meestal via pointers). Met de "malloc()" funktie bezetten we geheugen in de "heap". C houdt bij waar geheugen bezet werd, het is ook mogelijk om een geheugengebied terug vrij te geven, welke gaten in de "heap" achter laat. Bij het opnieuw bezetten van geheugen zullen deze gaten, indien mogelijk, terug opgevuld worden. 11.4 De "sizeof()" funktie.Met "sizeof()" kunnen we de grootte in bytes berekenen van een variabele, struktuur, enz. In dit voorbeeld gebruiken we de funktie in kombinatie met "malloc", om geheugen te bezetten welke juist voldoende plaats inneemt om de struktuur in te bewaren. 11.5 Wat is een "cast"?Voor de "malloc" funktie hebben we nog een vreemd uitziende konstruktie, dit noemt men een "cast". Standaard geeft "malloc()" een "pointer" terug welke naar een karakter wijst (omdat een "char" 1 byte groot is), maar vaak hebben we geen "pointer" naar een karakter nodig. Het is echter mogelijk om de "pointer" welke "malloc()" teruggeeft om te rekenen naar een "pointer" welke wijst naar het datatype dat we in ons programma nodig hebben. Dit is precies wat deze "rare" konstruktie doet, het vertelt C dat we een "pointer" nodig naar een struktuur die er uitziet zoals "dier". 11.6 Het gebruiken van dynamisch aangemaakt geheugen.Indien je de teorie over "pointers" en "strukturen" goed begrepen hebt moet de rest van het programma vrij duidelijk zijn. Via de "pointers" "pt1", "pt2" en "pt3" worden er dynamisch strukturen in het geheugen aangemaakt, via deze pointers wordt er ook data in deze strukturen geplaatst zoals we in het vorige hoofdstuk gezien hebben. 11.7 Het terug vrijmaken van geheugen met "free()".Een andere nieuwe instruktie in het programma is "free(pt)", waar "pt" een "pointer" is naar het geheugenblok dat we terug wensen vrij te geven. Nadat in het programma de data afgeprint is stellen we "pt1" gelijk aan "pt3", hierdoor hebben we dus geen "pointer" meer naar het datagebied waar "pt1" naar wees. We kunnen dit datagebied dus ook niet meer vrijgeven! Dit voorbeeld is dit echter geen probleem, bij het verlaten van het programma wordt al het eerder bezette geheugen terug vrijgegeven. 11.8 Een array van pointers.Het volgende programma is een andere voorbeeld van dynamisch geheugen, hier maken we gebruik van array van pointers.
We beginnen het programma met het "malloc.h" aan ons programma toe te voegen, dit is echter niet bij alle C-kompilers nodig. We gebruiken dezelfde struktuur als in het voorgaande programma, maar deze keer maken we array van 12 "pointers" plus een ekstra "pointer" aan. Met } *pt[12],*point; maken deze aan. Zoals we als gezien hadden is een array al een "pointer", dus is bv. "pt[0]" een "pointer" naar een "pointer" welke het begin van de struktuur aanwijst. Dit komt in C vaker voor zo is bij "int ****pt", "pt" een "pointer" naar een "pointer" naar een "pointer" naar een "pointer" welke een integer aanwijst. De rest van het programma komt vrij bekend over, nu we 12 pointers hebben gebruiken we een "for" lus om dynamisch struktuur variabelen aan te maken. Met de volgende "for" plaatsen we wat data in deze strukturen, daarna passen we via "pointers" enkele leeftijden aan. Met de voorlaatste "for" lus drukken we de velden van de strukturen af. De laatste "for" lus dient om het bezette geheugen terug vrij te geven, dit was in principe niet nodig vermits bij het verlaten van het programma dit geheugen toch terug vrijgegeven zou worden. Het echter een goede gewoonte om telkens indien we eerder bezet geheugen niet meer nodig hebben dit terug vrij te geven. 11.9 Een gelinkte lijstBij een gelinkte lijst of "linked list" in het engels wijst het eerste element (via een "pointer") de tweede aan, het tweede element wijst op zijn beurt naar het derde, de derde naar het vierde, ... enz ... Het volgende programma is hier een voorbeeld van.
Het programma start op ongeveer dezelfde manier als de vorige programma's. We met het definiëren van een konstante "RECORDS" welke het aantal variabelen aan geeft. Hierna maken we een struktuur aan welke er bijna hetzelfde uitziet als in de twee vorige programma's, enkel hebben we via struct dier *volgende een ekstra "pointer" in de struktuur opgenomen. Deze zal het adres bevatten van de volgende variabele. We definiëren drie "pointers" naar deze struktuur plus een integer "index" welke we later zullen gebruiken als teller. Via de "malloc" funktie maken zoals in de vorige voorbeeld een eerste variabele aan, het adres van deze variabele wordt in de pointer "start" bewaard. Dit adres onthouden we ook de pointer "vorige". Met de eerste "for" lus maken we onze lijst aan, telkens de lus doorlopen wordt bezetten we geheugen, kopiëren we data in de juist aangemaakte variabele, en vullen we de pointers met de juiste adressen. De pointer "vorige" bevat het adres van de vorige variabele, met vorige->volgende=point kopiëren we het adres van de nieuwe struktuur in de pointer van de oude. Telkens we een nieuwe struktuur variabele aan maken stellen we de pointer hiervan gelijk aan "NULL", dit doen we om het einde van de lijst aan te geven. We doorlopen de lus zes keer, na het einde van de deze "for" hebben we een lijst aangemaakte welke er zo uitziet. 1. De pointer "start" wijst naar de eerste struktuur in de lijst. 2. Elke struktuur bevat een pointer welke naar de volgende struktuur wijst. 3. De laatste struktuur bevat een pointer welke gelijk is aan "NULL", hiermee geven we het einde van de lijst aan.
Het is duidelijk dat we via een dergelijke opbouw het niet mogelijk is om naar bv. het derde element in de lijst te gaan. De enige manier om dit element te bereiken is via het eerste te beginnen, en zo in de lijst "af te dalen". Niet alle datatypes zijn dus geschikt om bewaard te worden in een dergelijke lijst, omdat het te veel tijd vraagt om zo een bepaald element op te zoeken. In principe kunnen we de strukturen in de lijst zo aanpassen dat ze twee pointers bevatten, één naar het volgende element, en een andere naar het vorige om het opzoekingswerk te vereenvoudigen. Met de "do ... while" lus drukken we de data op het scherm, de metode die we hiervoor gebruiken is analoog met hoe we de data genereert hebben. De lus wordt doorlopen totdat de pointer van de struktuur gelijk is aan "NULL". Met de laatste "for" lus geven we het bezetten geheugen terug, dit was ook nu niet echt nodig, maar het is een goede gewoonte dit wel te doen. Ja ... Ik val in herhaling ;-) 11.10 De "calloc" funktie.De "calloc" funktie lijkt hard op de "malloc" funktie, enkel vult het de bezette datablok met nullen, wat praktisch kan zijn in sommige gevallen. 11.11 Programmeer opdrachten!1.Herschrijf het eerste programma van hoofdstuk 10 zo dat de twee strukturen dynamische aangemaakt worden. 2.Herschrijf het tweede programma van hoofdstuk 10 zo dat de 12 strukturen dynamisch worden aangemaakt. 12. Karakter & bit manipulatieIn dit laatste hoofdstuk gaan we nog enkel funkties bekijken voor karakter en bit manipulatie. Zo beschikt C over funkties voor konversie naar grootte en kleine letters, schuif ("shift") instrukties... 12.1 Grote & kleine letters.Grootte en kleine letters hebben een andere ASCII - waarde, zo komt "a" overeen met 97 en "A" met 65. Dit kan erg vervelend zijn voor sommige toepassingen zoals bv. sorteren. C heeft hiervoor enkele funkties om deze problemen op te lossen.
Het volgende programma illustreert deze vier funkties, het leest een bestand regel per regel en drukt dit bestand op het scherm af. Alle grote letters worden verandert in kleine, alle kleine letters worden verandert grootte. Indien je de vorige hoofdstukken goed begrepen hebt zal het niet moeilijk zijn de werking van het programma te begrijpen.
12.2 Verschillende soorten van karakters.C maakt onderscheidt tussen bepaalde soorten van karakters, zo hebben we bv. kontrole karakter welke we al in "printf" gebruikt hebben. Misschien heb je al afgevraagd hoe we een ' " ' met "printf" kunnen afdrukken, zonder dit aanzien wordt als - einde van de data -, of hoe het mogelijk we is een "\" af te drukken. Ook hier is een oplossing voor onderstaande lijst vervolledig de lijst van kontrole karakters.
C heeft funkties om te testen met wat voor type van karakter we te maken hebben.
Het volgende programma laadt opnieuw een bestand in, en telt per regel het aantal karakters, cijfers en spaties.
12.3 Logische bewerkingen.In de volgende lijst staan enkele logische bewerken:
Het volgende programma demonstreert bovenstaande bewerkingen.
12.4 Schuif bewerkingen.Ook is het mogelijk om de bits van een getal naar links of rechts te verschuiven, dit gebeurt op de volgende manier.
Het volgende programma illustreert dit.
13. Gebruikte Software
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| © 2012 Staf Wagemakers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||