Les 2
Compileren met gcc
Van broncode naar uitvoerbaar programma: gcc, waarschuwingen, compileren en linken.
🎯 Na deze les kun je
- een C-programma compileren en uitvoeren met gcc
- uitleggen wat de compiler en wat de linker doet
- waarschuwingen aanzetten met -Wall -Wextra en ze lezen
- de fout undefined reference herkennen en oplossen
In Python typ je python3 kassabon.py en het programma draait. In C gaat dat anders: eerst vertaalt een compiler je broncode naar machinecode, en pas daarna kun je het resultaat uitvoeren. Die extra stap voelt in het begin omslachtig, maar hij levert snelle programma's op en de compiler vangt veel fouten voordat je programma ooit draait.
In deze les compileer je je eerste C-programma met gcc, zet je waarschuwingen aan en leer je het verschil tussen compileren en linken.
Hallo wereld in C
#include <stdio.h>
int main(void) {
printf("Hallo, wereld!\n");
return 0;
}
Regel voor regel:
#include <stdio.h>haalt de declaraties van de standaard in- en uitvoerfuncties binnen, waaronderprintf.int main(void)is het startpunt. Elk C-programma heeft precies éénmain.voidbetekent: geen parameters.printfschrijft tekst.\nis een nieuwe regel; die komt er niet vanzelf bij zoals bij Python'sprint.return 0;geeft de exitcode terug aan het besturingssysteem.0betekent "gelukt".- Elke opdracht eindigt met een puntkomma, en blokken staan tussen
{ }in plaats van inspringing.
Bewaar dit als hallo.c en compileer:
gcc hallo.c -o hallo
./hallo
-o hallo bepaalt de naam van het uitvoerbare bestand (executable). Zonder -o heet het a.out.
Waarschuwingen aanzetten: -Wall -Wextra
Standaard is gcc opvallend stil. Veel verdachte code compileert zonder één melding. Zet daarom altijd waarschuwingen aan:
gcc -std=c17 -Wall -Wextra -o hallo hallo.c
| Optie | Betekenis |
|---|---|
-std=c17 |
gebruik de C17-standaard |
-Wall |
de belangrijkste waarschuwingen |
-Wextra |
nog een flinke set extra waarschuwingen |
-Werror |
behandel waarschuwingen als fouten (handig in een lab of CI) |
-g |
voeg debug-informatie toe (voor gdb, les 14) |
Voorspel eerst: wat zegt gcc over dit programma?
#include <stdio.h>
int main(void) {
int totaal = 12;
printf("Totaal: %d euro\n", totaal);
int ongebruikt; /* FOUT: wordt nooit gebruikt */
return 0;
}
Met -Wall krijg je:
hallo.c:6:9: warning: unused variable 'ongebruikt' [-Wunused-variable]
Een melding heeft altijd dezelfde vorm: bestand:regel:kolom: soort: tekst. Begin bij de eerste melding; latere meldingen zijn vaak een gevolg daarvan.
Een waarschuwing is geen fout: het programma wordt toch gebouwd. Maar behandel elke waarschuwing alsof het een fout is. In C zijn waarschuwingen vaak echte bugs.
Compileren versus linken
gcc hallo.c -o hallo lijkt één stap, maar het zijn er eigenlijk vier:
- Preprocessor: verwerkt
#includeen#define(plakt de header letterlijk in je bestand). - Compiler: vertaalt C naar assembly.
- Assembler: maakt er een objectbestand (
.o) van: machinecode, maar nog niet compleet. - Linker: plakt objectbestanden en bibliotheken samen tot één executable en koppelt aanroepen (zoals
printf) aan de echte code.
Je kunt de stappen los zien:
gcc -c hallo.c # alleen compileren → hallo.o
gcc hallo.o -o hallo # alleen linken → hallo
Bij één bestand merk je dit niet. Zodra je project uit meerdere bestanden bestaat (les 5) wordt het belangrijk: elk .c-bestand wordt los gecompileerd en de linker brengt ze samen.
Uitgewerkt voorbeeld: een kassabon
#include <stdio.h>
int main(void) {
double koffie = 3.20;
double appeltaart = 4.50;
int aantal_koffie = 2;
double totaal = aantal_koffie * koffie + appeltaart;
printf("2x koffie %6.2f\n", aantal_koffie * koffie);
printf("1x appeltaart %6.2f\n", appeltaart);
printf("Totaal %6.2f\n", totaal);
return 0;
}
Voorspel de uitvoer voordat je het draait. %6.2f betekent: een kommagetal, 6 tekens breed, 2 cijfers achter de punt.
2x koffie 6.40
1x appeltaart 4.50
Totaal 10.90
Typische fouten
undefined reference. Dit is een linkerfout, geen compilerfout:
#include <stdio.h>
double bereken_btw(double bedrag); /* FOUT: alleen gedeclareerd, nergens gedefinieerd */
int main(void) {
printf("%.2f\n", bereken_btw(100.0));
return 0;
}
/usr/bin/ld: /tmp/cc1.o: in function `main':
hallo.c:(.text+0x15): undefined reference to `bereken_btw'
collect2: error: ld returned 1 exit status
De compiler was tevreden (hij kende de declaratie), maar de linker kon de functie zelf niet vinden. Oorzaken: tikfout in de naam, de definitie vergeten, of een .c-bestand niet meegeven aan gcc. Herkenbaar aan ld en collect2 in de melding.
implicit declaration. Je gebruikt printf maar vergat #include <stdio.h>. Moderne gcc-versies maken hier een fout van.
Puntkomma vergeten. De melding wijst vaak naar de regel ná de echte fout: expected ';' before 'return'.
Oefeningen
- Compileer het kassabonvoorbeeld met
-Wall -Wextra. Voeg een derde product toe (thee, 2,80) en pas het totaal aan. - Haal
#include <stdio.h>weg en compileer opnieuw. Lees de melding: is dit een compiler- of linkerfout? - Laat
mainreturn 3;doen. Draai het programma en typ daarnaecho $?. Wat zie je, en waarom?
Modelantwoord
- Voeg toe:
double thee = 2.80;, een extraprintf("1x thee %6.2f\n", thee);en tel+ theebij het totaal op. Het totaal wordt13.70. - Je krijgt
error: implicit declaration of function 'printf'(met een hint om<stdio.h>toe te voegen). Het is een compilerfout: de compiler kentprintfniet. Er zit geenldin de melding. echo $?toont3: de exitcode van het laatst uitgevoerde programma. Scripts gebruiken dit om te zien of iets gelukt is (0) of niet (alles behalve 0).
Lab: kassabon in C compileren
- Open
kassabon.cin de lab-omgeving. Het compileert nu met twee waarschuwingen. - Compileer met
gcc -std=c17 -Wall -Wextra -Werror -o kassabon kassabon.cen los alle meldingen op. - Zorg dat het programma het totaal met twee decimalen afdrukt en eindigt met exitcode 0.
beetje check c-cpp-les2
Samenvatting
- C is een gecompileerde taal: eerst
gcc, dan./programma. - Compileer altijd met
-Wall -Wextra(en in labs-Werror). - Compileren maakt objectbestanden, linken plakt ze samen.
undefined referenceis een linkerfout. - Een foutmelding lees je als bestand:regel:kolom, en je begint bij de eerste.
In de volgende les kijk je naar typen: waarom C precies wil weten hoeveel geheugen een variabele inneemt.
Lab
Kassabon in C compileren
💻 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-les2
Klaar? Controleer je werk met:
$ beetje check c-cpp-les2