Beetje Coderen

Les 5

Functies en header-bestanden

Prototypes, header guards, meerdere bronbestanden en pass-by-value.

⏱ 45 minuten leestijd + oefenen

🎯 Na deze les kun je

  • functies met prototypes declareren en definiëren
  • een eigen module maken met een .h- en een .c-bestand
  • header guards gebruiken
  • uitleggen wat pass-by-value betekent

Functies ken je: een stuk code met een naam, parameters en een returnwaarde. In C komt daar één ding bij: de compiler leest je bestand van boven naar beneden en wil een functie kennen voordat je haar aanroept. Daarvoor bestaan prototypes en header-bestanden. Daarmee kun je je programma ook over meerdere bestanden verdelen.

Een functie in C

#include <stdio.h>

double met_btw(double bedrag, double percentage) {
    return bedrag * (1.0 + percentage / 100.0);
}

int main(void) {
    printf("%.2f\n", met_btw(10.0, 21.0));
    return 0;
}

Vóór de naam staat het returntype, en elke parameter heeft een type. Een functie die niets teruggeeft heeft returntype void.

Prototypes

Zet je met_btw onder main, dan kent de compiler haar nog niet op het moment van de aanroep. Oplossing: een prototype (declaratie) bovenaan:

#include <stdio.h>

double met_btw(double bedrag, double percentage);   /* prototype */

int main(void) {
    printf("%.2f\n", met_btw(10.0, 9.0));
    return 0;
}

double met_btw(double bedrag, double percentage) {  /* definitie */
    return bedrag * (1.0 + percentage / 100.0);
}

Een declaratie zegt "deze functie bestaat, met deze typen". Een definitie bevat de code. Je mag iets vaak declareren, maar maar één keer definiëren.

Een eigen module: .h en .c

Prototypes die je in meerdere bestanden nodig hebt, zet je in een header (.h). De code komt in een .c-bestand.

btw.h:

#ifndef BTW_H
#define BTW_H

double met_btw(double bedrag, double percentage);
double btw_bedrag(double bedrag, double percentage);

#endif

btw.c:

#include "btw.h"

double met_btw(double bedrag, double percentage) {
    return bedrag + btw_bedrag(bedrag, percentage);
}

double btw_bedrag(double bedrag, double percentage) {
    return bedrag * percentage / 100.0;
}

main.c:

#include <stdio.h>
#include "btw.h"

int main(void) {
    printf("Btw op 50 euro: %.2f\n", btw_bedrag(50.0, 21.0));
    return 0;
}

Compileer en link beide bronbestanden:

gcc -Wall -Wextra -c btw.c        # → btw.o
gcc -Wall -Wextra -c main.c       # → main.o
gcc btw.o main.o -o kassa         # linken

of in één keer: gcc -Wall -Wextra btw.c main.c -o kassa. Header-bestanden geef je niet mee aan gcc; die worden via #include binnengehaald.

#include <...> zoekt in de systeemmappen, #include "..." eerst in de map van je eigen bestand.

Header guards

#include plakt de header letterlijk in je bestand. Wordt dezelfde header twee keer ingevoegd (bijvoorbeeld via een andere header), dan kun je dubbele definities krijgen. De drie regels #ifndef BTW_H / #define BTW_H / #endif zorgen dat de inhoud maar één keer meetelt. Veel compilers kennen ook #pragma once, maar dat is geen officiële standaard.

Pass-by-value

Voorspel de uitvoer:

#include <stdio.h>

void verdubbel(int getal) {
    getal = getal * 2;
}

int main(void) {
    int x = 5;
    verdubbel(x);
    printf("%d\n", x);
    return 0;
}

Het antwoord is 5. C geeft argumenten altijd by value door: de functie krijgt een kopie. Wil je dat een functie de variabele van de aanroeper wijzigt, dan heb je een pointer nodig (les 6), of je geeft de nieuwe waarde terug: x = verdubbel(x);.

Typische fouten

  • implicit declaration of function: je roept een functie aan zonder prototype of zonder de header te includen.
  • multiple definition of (linker): je hebt een functiedefinitie in een header gezet die door twee .c-bestanden wordt geïnclude. In headers horen alleen declaraties.
  • undefined reference: je vergat btw.c mee te compileren.

Oefeningen

  1. Voeg aan de btw-module een functie double zonder_btw(double bedrag_incl, double percentage) toe en test haar met 121 euro bij 21%.
  2. Waarom levert een header zonder guards die alleen prototypes bevat meestal geen fout op, maar één met een struct-definitie wel?
  3. Schrijf int max3(int a, int b, int c) met een prototype boven main en de definitie eronder.
Modelantwoord
  1. return bedrag_incl / (1.0 + percentage / 100.0); in btw.c en het prototype in btw.h. Resultaat: 100.00.
  2. Een declaratie mag vaker voorkomen, een definitie niet. Een struct-definitie die twee keer in hetzelfde bestand belandt geeft redefinition of 'struct ...'.
  3. Een mogelijke uitwerking:
#include <stdio.h>

int max3(int a, int b, int c);

int main(void) {
    printf("%d\n", max3(4, 9, 2));
    return 0;
}

int max3(int a, int b, int c) {
    int m = a;
    if (b > m) m = b;
    if (c > m) m = c;
    return m;
}

Lab: btw-functies in een eigen module

  1. Maak btw.h (met header guard) en btw.c met met_btw, btw_bedrag en zonder_btw.
  2. Laat main.c een kassabon met drie producten afdrukken: bedrag excl., btw en incl.
  3. Bouw met gcc -Wall -Wextra -Werror btw.c main.c -o kassa.
  4. beetje check c-cpp-les5

Samenvatting

  • Een functie moet gedeclareerd zijn vóór de aanroep: via een prototype of een header.
  • Een module = .h (declaraties, met header guard) + .c (definities).
  • Je compileert alle .c-bestanden en linkt ze samen; headers geef je niet mee.
  • C kopieert argumenten (pass-by-value).

Volgende les: pointers, waarmee een functie wél de variabele van de aanroeper kan wijzigen.

Lab

Btw-functies in een eigen module

🔒 Het live lab hoort bij de volledige cursus. Met één aankoop (€ 29,00, eenmalig) krijg je alle lessen, de live labs en het certificaat. Geen abonnement. Je kunt dit lab altijd gratis lokaal met Docker doen.

Cursus kopen en verdergaan

💻 Lokaal met Docker. Start de lab-container met (geeft de opdracht een eigen startcommando, gebruik dan dat):

$ docker run -it --rm -e BEETJE_COURSE=c-cpp -e BEETJE_LESSON=5 registry.wizg.it/webhosting/beetjecoderen-web:lab-c-latest

Krijg je unauthorized bij het downloaden? De registry vraagt een login. Bouw het image dan eerst zelf (eenmalig, een paar minuten):

$ docker build -t registry.wizg.it/webhosting/beetjecoderen-web:lab-c-latest -f c.Dockerfile https://beetjecoderen.nl/labs/lab-images.tar.gz

In de container: beetje lijst toont alle lessen van het image.

Volg daarna de stappen van de opdracht hierboven. Nog geen Docker? Zie les 14 van Een beetje Linux & Docker.

Klaar? Controleer je werk met:

$ beetje check c-cpp-les5

📝 Quiz: functies en header-bestanden

5 vragen · 70% nodig om te slagen

1. Wat staat er in een header-bestand?
2. Waarvoor dienen #ifndef/#define/#endif in een header?
3. Een functie zet haar parameter op 10. Verandert de variabele van de aanroeper?
4. Hoe compileer je een programma met main.c en btw.c?
5. Wat is het verschil tussen #include <x.h> en #include "x.h"?

Dit was de laatste gratis les

Je hebt les 1–5 van Een beetje C & C++ gratis gevolgd. Wil je verder? Met één aankoop (€ 29,00, eenmalig) krijg je de overige 30 lessen, de live labs en het certificaat. Geen abonnement.

Cursus kopen en verdergaan