Programmeren in C/Datatypes: verschil tussen versies

Verwijderde inhoud Toegevoegde inhoud
JorisV (overleg | bijdragen)
Sephiroth (overleg | bijdragen)
kGeen bewerkingssamenvatting
Regel 106:
 
Floating point representaties missen deze eigenschap. Dit heeft tot gevolg dat een floating point variable '''niet''' elke denkbare waarde kan hebben en '''niet''' exact is. Vooral onder beginners leidt dit vaak tot problemen. Bijvoorbeeld...
{{code
 
|Taal= C
|Titel=
|Code=
<source lang="c">
#include <stdio.h>
Regel 127 ⟶ 130:
}
</source>
}}
 
Bovenstaande code zal niet het resultaat opleveren dat de naïeve programmeur ervan verwacht, ondanks het feit dat 3*(1/3) wel degelijk 1 is. Dit ligt aan het feit dat een binaire representatie van 1/3 als floating point onmogelijk is en er dus een fout wordt geintroduceerd. Een hele kleine fout weliswaar (afhankelijk van de precisie van ''double'' op heet platform in kwestie, maar genoeg om de ''is gelijk''-operator te ondermijnen. Over het algemeen kan men stellen dat het gebruik van de ''=='' (is-gelijk) operator met twee floating-point getallen uit den boze is.
 
De gebruikelijke oplossing ziet er (ongeveer) als volgt uit.
{{code
 
|Taal= C
|Titel=
|Code=
<source lang="c">
#include <math.h>
Regel 156 ⟶ 162:
}
</source>
}}
 
En "''Hey, Presto!''". De code werkt zoals het de bedoeling is omdat we nu rekening houden met de inherente fout die het gebruik van floating-point getallen oplevert. Met behulp van "''fabs(x/3 - 1.0) < EPSILON''" wordt het absolute verschil uitgerekend en als dit kleiner is dan een voorgedefinieerd maximum ('''EPSILON''') zijn de twee waarden "gelijk" of in ieder geval "gelijk genoeg". In de wandeling staat deze techniek ook wel bekend als "''close-enough comparison''". Een goede waarde voor EPSILON hangt af van de toepassing en de implementatie van de compiler (dus "''[[:w:RTFM|RTFM]]''", nogmaals).
 
==Vormen van getallen==
Vormen van getallen zijn:
:*int <BR>
:*double <BR>
:*long <BR>
:*float <BR>
:*short <BR>
:*char <BR>
<BR>
 
Hierbij bevatten long, int en short gehele getallen, waarvoor geldt dat long groter of gelijk aan (in aantal bits) een int is en waarbij een int groter dan of gelijk aan een short is. Hoe deze vertaling juist gebeurt hangt sterk af van het onderliggende platform. Een 32 bit Linux heeft bijvoorbeeld 32 bit voor een int terwijl dit onder DOS wel 16 bit is. Een ''char'' is typisch nog kleiner en heeft de grootte van 1 byte, de bedoeling van een char is een ascii teken te kunnen bevatten.
 
Een float en een double kunnen reële getallen bevatten waarbij een double een groter bereik en precisie heeft dan een float.
 
Alle types getallen hebben een ''signed'' en een ''unsigned'' variant, dit sleutelwoord wordt voor de variabele geplaatst. Bijvoorbeeld
{{code
|Taal= C
|Titel=
|Code=
<source lang=c>unsigned int i;</source>
}}
Dit declareert een integer zonder tekenbit, dit wil zeggen dat een extra bitpositie vrijkomt voor ''nuttigere'' informatie en dat tweemaal zo grote getallen opgeslagen kunnen worden.
 
Als je een variabele van het type unsigned maakt, kan hij 2 keer zo grote getallen bevatten als signed, maar alleen positieve. Dit is omdat je het eerste bit (het teken bit) opoffert, je gaat er van uit dat de getallen standaard positief zijn. En vermits per extra bit het aantal mogelijke waarden verdubbled kan men dus dubbel zo grote getallen opslaan.
 
Een makkelijke manier om te weten komen hoeveel bytes gereserveerd zijn voor een bepaald datatype kan met behulp van de ''sizeof()'', deze wordt tijdens het compileren vervangen door de grootte die gereserveerd wordt voor het datatype (in aantal bytes).
{{code
|Taal= C
|Titel=
|Code=
<source lang=c>
#include <stdio.h>
int main(){
printf("char :%d\n", sizeof(char));
printf("short :%d\n", sizeof(short));
printf("int :%d\n", sizeof(int));
printf("long :%d\n", sizeof(long));
printf("long long :%d\n", sizeof(long long));
printf("float :%d\n", sizeof(float));
printf("double :%d\n", sizeof(double));
printf("void * :%d\n", sizeof(void *));
return 0;
}
</source>
}}
De uitvoer op een 32 bit Linux systeem is (uit deze uitvoer volgt automatisch dat het een 32 bit systeem is, kijk maar naar de grootte van een pointer, deze is 4 byte, dus 32 bit):
 
<pre>
char :1
short :2
int :4
long :4
long long :8
float :4
double :8
void * :4
</pre>
 
== ''enum'' ==
Een ''enum'' (kort voor ''enumerated type'') is een "opgesomd type". Dat wil zeggen dat alle mogelijke waarden voor zo'n type expliciet worden opgesomd en andere waarden domweg niet bestaan. Dit kan bijzonder handig zijn in die gevallen waar slechts een beperkt aantal waarden nodig is.
{{code
 
|Taal= C
|Titel=
|Code=
<source lang="c">
enum kleur
Regel 170 ⟶ 237:
};
</source>
}}
 
Deze code definieert een datatype 'kleur' als een opsomming met drie mogelijke waarden. Aan elk van deze waarden wordt een numerieke waarde toegekend, maar welke waarde dit precies is, is hier ongedefiniëerd. Als het wenselijk is dat deze drie een specifieke waarde hebben, kan dat ook:
{{code
 
|Taal= C
|Titel=
|Code=
<source lang="c">
enum kleur
Regel 181 ⟶ 251:
};
</source>
}}
De waarde '''Rood''' is nu equivalent aan '''0''', groen aan '''1''' en blauw aan '''2'''. Let wel dat het daarmee nog steeds geen ''int'' is, maar een afzonderlijk datatype.
{{code
 
|Taal= C
|Titel=
|Code=
<source lang="c">
enum kleur
Regel 199 ⟶ 272:
k = 21231; /* FOUT, hoewel de compiler er geen error op zal genereren. */
</source>
}}
 
Het type dat de ''enum'' representeert is sterk van de compiler afhankelijk. Dit '''kan''' een ''int'' zijn, maar het type is compiler-afhankelijk. De toekenning "''k=21231''" neemt aan dat dit type groot genoeg is om 21231 te bevatten, maar er bestaat geen garantie dat dat inderdaad zo is. Bovendien is het doel van een enum een type te definieren dat alleen bepaalde waarden kan bevatten, als we daar dan allerlei andere waarden in gaan stoppen, zetten we onze medeprogrammeurs op het verkeerde been en de fouten zijn voorgeprogrammeerd. Niet goed, niet doen, dus.
 
== ''void'' ==
Het type ''void'' ("leeg") is een wat bijzonder geval aangezien dit type geen waarde aanduidt. Men kan dan ook geen variabele met het type ''void'' declareren. Het type wordt voornamelijk gebruikt om aan te geven dat een functie geen waarde teruggeeft of geen argumenten verwacht. Het is niet mogelijk een variabele van het type ''void'' te declareren, om de hele simpele reden dat dit type geen afmeting heeft en in 0 bits laten zich nu eenmaal geen waarden opslaan.
{{code
 
|Taal= C
|Titel=
|Code=
<source lang="c">
/* Dit is een functie die geen argumenten heeft en geen waarden teruggeeft. */
Regel 212 ⟶ 288:
}
</source>
}}
 
== ''Pointers'' ==
Een ''pointer'' ("wijzer") in ''C'' is, simpel gezegd, een geheugen-adres. Een ''pointer'' wijst dus naar een geheugen gebied waar een bepaalde waarde hoort te staan. Of deze waarde er inderdaad staat, is overigens volledig de verantwoordelijkheid van de programmeur. Pointers zijn, net als integers ''floating-point'' types ''scalars''. Dat wil zeggen dat de normale arithmatische bewerkingen (zoals optellen en aftrekken) gebruikt kunnen worden, zij het dat deze bewerkingen een wat bijzondere opvatting over het resultaat van een optelling hebben.
Regel 219 ⟶ 295:
 
Een bijzonder type ''pointer'' is de ''void pointer'', een "wijzer naar niets". Dit houdt niet in dat het geheugengebied waarnaar dit type wijst leeg is, maar het type ervan is niet gespecificeerd. In de praktijk betekent dit dat elke willekeurige ''pointer'' aan een ''void pointer'' kan worden toegekend, maar dat een ''void pointer'' nooit zonder expliciete ''type-cast'' aan een ander type pointer kan worden toegekend. Het gebruik van ''void pointers'' kan daarom bijzonder handig zijn, maar (vanwege de expliciete cast) is het mogelijk hiermee zelfs het zeer tolerante type-checking systeem van ''C'' volledig buiten werking gesteld kan worden.
{{code
 
|Taal= C
|Titel=
|Code=
<source lang="c">
void foobar(void)
Regel 242 ⟶ 321:
}
</source>
}}
 
Om aan te geven dat een pointer '''nergens''' naar wijst, kan een '''0''' (of, wat netter, ''NULL'') aan die pointer worden toegekend. Het resultaat ervan is dat de pointer een ''null-pointer'' wordt, een bijzondere waarde die, als de pointer wordt gevolgd (''gedereferenced'' in vakjargon) veelal in een exceptie resulteert. Officieel is dit echter niet meer dan ''undefined behavior'', ongedefinieerd gedrag. Dit verschijnsel is een veelvoorkomende oorzaak van programma-''crashes''. De representatie van een ''null-pointer'' is overigens volledig compiler afhankelijk, de enige voorwaarde die de standaard stelt is dat het geen geldige waarde voor een pointer mag zijn.
 
Informatie afkomstig van https://nl.wikibooks.org Wikibooks NL.
Wikibooks NL is onderdeel van de wikimediafoundation.