Beetje Coderen

Les 2

Compileren met gcc

Van broncode naar uitvoerbaar programma: gcc, waarschuwingen, compileren en linken.

⏱ 40 minuten leestijd + oefenen

🎯 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, waaronder printf.
  • int main(void) is het startpunt. Elk C-programma heeft precies één main. void betekent: geen parameters.
  • printf schrijft tekst. \n is een nieuwe regel; die komt er niet vanzelf bij zoals bij Python's print.
  • return 0; geeft de exitcode terug aan het besturingssysteem. 0 betekent "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:

  1. Preprocessor: verwerkt #include en #define (plakt de header letterlijk in je bestand).
  2. Compiler: vertaalt C naar assembly.
  3. Assembler: maakt er een objectbestand (.o) van: machinecode, maar nog niet compleet.
  4. 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

  1. Compileer het kassabonvoorbeeld met -Wall -Wextra. Voeg een derde product toe (thee, 2,80) en pas het totaal aan.
  2. Haal #include <stdio.h> weg en compileer opnieuw. Lees de melding: is dit een compiler- of linkerfout?
  3. Laat main return 3; doen. Draai het programma en typ daarna echo $?. Wat zie je, en waarom?
Modelantwoord
  1. Voeg toe: double thee = 2.80;, een extra printf("1x thee %6.2f\n", thee); en tel + thee bij het totaal op. Het totaal wordt 13.70.
  2. Je krijgt error: implicit declaration of function 'printf' (met een hint om <stdio.h> toe te voegen). Het is een compilerfout: de compiler kent printf niet. Er zit geen ld in de melding.
  3. echo $? toont 3: 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

  1. Open kassabon.c in de lab-omgeving. Het compileert nu met twee waarschuwingen.
  2. Compileer met gcc -std=c17 -Wall -Wextra -Werror -o kassabon kassabon.c en los alle meldingen op.
  3. Zorg dat het programma het totaal met twee decimalen afdrukt en eindigt met exitcode 0.
  4. 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 reference is 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

Volg daarna de stappen van de opdracht hierboven. Nog geen Docker? Zie les 14 van Een beetje Linux & Docker. Voor dit lab is er geen live lab in de browser.

Klaar? Controleer je werk met:

$ beetje check c-cpp-les2

📝 Quiz: compileren met gcc

5 vragen · 70% nodig om te slagen

1. Wat doet `gcc hallo.c -o hallo`?
2. Je krijgt `undefined reference to 'bereken_btw'`. Welke stap faalt?
3. Waarom compileer je met -Wall -Wextra?
4. Wat betekent `return 0;` aan het eind van main?
5. Wat doet `gcc -c rekenen.c`?