Les 3
Typen en geheugen in C
int, long, char, float en double: hoeveel geheugen een waarde inneemt en wat er gebeurt als hij niet past.
🎯 Na deze les kun je
- de basistypen van C kiezen voor een gegeven situatie
- met sizeof bepalen hoeveel bytes een type inneemt
- waarden afdrukken en inlezen met de juiste printf/scanf-specifiers
- integer overflow herkennen
In Python schrijf je temperatuur = 21.5 en Python regelt de rest. In C moet je bij elke variabele zeggen wat voor soort waarde erin komt. Dat type bepaalt twee dingen: hoeveel bytes geheugen de variabele krijgt en hoe die bytes gelezen worden.
Dat klinkt als extra werk, en dat is het ook. Maar het is precies waarom C zo dicht bij de machine zit: je weet altijd hoe je gegevens in het geheugen liggen.
De basistypen
int leeftijd = 34; /* geheel getal */
long inwoners = 17900000L; /* groter geheel getal */
char letter = 'A'; /* één teken (eigenlijk een klein getal) */
float prijs = 2.49f; /* kommagetal, enkele precisie */
double afstand = 384400.0; /* kommagetal, dubbele precisie */
| Type | Typische grootte (64-bit Linux) | Gebruik |
|---|---|---|
char |
1 byte | één teken of een klein getal |
int |
4 bytes | gewone gehele getallen (±2,1 miljard) |
long |
8 bytes | grote gehele getallen |
float |
4 bytes | kommagetal, ~7 cijfers precisie |
double |
8 bytes | kommagetal, ~15 cijfers precisie |
Elk geheel type bestaat ook als unsigned (alleen ≥ 0, dubbel zo groot bereik naar boven). Gebruik voor kommagetallen standaard double; float is alleen zinvol als geheugen echt krap is.
De groottes in de tabel zijn typisch, niet gegarandeerd. De C-standaard zegt alleen dat een int minstens 16 bits is. Op een microcontroller (cursus Embedded C) is int vaak 2 bytes. Wil je een vaste grootte, gebruik dan int32_t en vrienden uit <stdint.h>.
sizeof
Met sizeof vraag je de compiler hoeveel bytes iets inneemt:
#include <stdio.h>
int main(void) {
printf("char: %zu\n", sizeof(char));
printf("int: %zu\n", sizeof(int));
printf("long: %zu\n", sizeof(long));
printf("double: %zu\n", sizeof(double));
return 0;
}
sizeof levert een size_t op; die druk je af met %zu. sizeof(char) is per definitie 1.
printf en scanf
printf weet niet zelf welk type je meegeeft. Jij vertelt het met een format specifier:
| Specifier | Type |
|---|---|
%d |
int |
%ld |
long |
%u |
unsigned int |
%c |
char |
%f |
double (bij printf ook float) |
%.1f |
double met 1 decimaal |
%zu |
size_t |
scanf leest invoer en moet weten waar de waarde heen moet. Daarom staat er een & voor de variabele (wat dat precies is, zie je in les 6):
#include <stdio.h>
int main(void) {
double celsius;
printf("Temperatuur in graden Celsius: ");
if (scanf("%lf", &celsius) != 1) {
printf("Dat is geen getal.\n");
return 1;
}
printf("%.1f C = %.1f F\n", celsius, celsius * 9.0 / 5.0 + 32.0);
return 0;
}
Let op het verschil: bij scanf is %lf voor double en %f voor float. scanf geeft terug hoeveel waarden het gelezen heeft; controleer dat altijd.
Integer overflow
Voorspel de uitvoer:
#include <stdio.h>
#include <limits.h>
int main(void) {
unsigned char teller = 255;
teller = teller + 1;
printf("teller: %d\n", teller);
printf("INT_MAX: %d\n", INT_MAX);
return 0;
}
teller: 0
INT_MAX: 2147483647
Een unsigned char kan 0 t/m 255 bevatten. 255 + 1 past niet en "loopt rond" naar 0. Bij unsigned typen is dat netjes gedefinieerd gedrag. Bij signed typen (int, long) is overflow ongedefinieerd gedrag (undefined behavior): de compiler mag aannemen dat het nooit gebeurt, en je programma kan dan van alles doen. Python kent dit probleem niet: daar groeien gehele getallen gewoon mee.
Typische fouten
- Verkeerde specifier:
printf("%d", 3.5)drukt onzin af. Met-Wallkrijg jeformat '%d' expects argument of type 'int', but argument 2 has type 'double'. &vergeten bij scanf:scanf("%d", leeftijd)crasht vaak. Ook hier waarschuwt-Wall.- Gehele deling:
9 / 5is1, niet1.8. Schrijf9.0 / 5.0zodra je een kommagetal wilt. Daar komen we in les 4 op terug.
Oefeningen
- Breid het
sizeof-programma uit metfloat,unsigned intenlong long. - Een
intop jouw machine is 4 bytes. Wat isINT_MAX + 1volgens de standaard? Zoek ook uit watUINT_MAX + 1uis. - Schrijf een programma dat een lengte in centimeters inleest (geheel getal) en die als meters met twee decimalen afdrukt.
Modelantwoord
printf("float: %zu\n", sizeof(float));enzovoort. Typisch: float 4, unsigned int 4, long long 8.INT_MAX + 1is signed overflow en dus ongedefinieerd gedrag: er is geen "juist" antwoord.UINT_MAX + 1uis0: unsigned rekenen loopt netjes rond.- Een mogelijke uitwerking:
#include <stdio.h>
int main(void) {
int cm;
if (scanf("%d", &cm) != 1) {
return 1;
}
printf("%.2f m\n", cm / 100.0);
return 0;
}
Let op 100.0: met cm / 100 krijg je een gehele deling.
Lab: temperatuurtabel met de juiste typen
- Schrijf
tabel.cdat een tabel van -10 tot en met 40 graden Celsius afdrukt in stappen van 5, met daarnaast Fahrenheit op één decimaal. - Kies voor elke variabele een passend type en gebruik de juiste specifiers.
- Compileer zonder waarschuwingen met
-Wall -Wextra -Werror. beetje check c-cpp-les3
Samenvatting
- Elke variabele heeft een type; dat bepaalt grootte en betekenis van de bytes.
sizeofvertelt hoeveel bytes iets inneemt; groottes verschillen per platform.printfenscanfhebben de juiste specifier nodig;scanfwil een&.- Unsigned overflow loopt rond; signed overflow is ongedefinieerd gedrag.
Volgende les: operatoren en control flow, inclusief de beruchte gehele deling.
Lab
Temperatuurtabel met de juiste typen
💻 Dit lab doe je op je eigen computer met Docker. Start de lab-container met (geeft de opdracht een eigen startcommando, gebruik dan dat):
$ docker run -it --rm registry.wizg.it/webhosting/beetje-labs:c-cpp-les3
Klaar? Controleer je werk met:
$ beetje check c-cpp-les3