Functies
Een van de grootste voordelen van programmeren is dat je code kunt herbruiken. Stel dat je een rechthoek op het scherm wil tekenen. We kunnen dit doen met de functie printf(). Bijvoorbeeld een rechthoek van 12 op 5 sterretjes kunnen we opvatten als 5 rijen van 12 sterretje onder elkaar. 5 keer hetzelfde herhalen vraagt om een for-lus:
#include <stdio.h>
main(){
int i = 0;
for (i = 0; i < 5; i++){
printf("%c%c%c%c%c%c%c%c%c%c%c%c\n",'*','*','*','*','*','*','*','*','*','*','*','*');
}
}
Deze code zal een mooie rechthoek op het scherm zetten. Ik vind de printf() zelf voor verbetering vatbaar. Uiteindelijk doen we hier ook 12 keer hetzelfde. Dit moet in een for-lus kunnen:
#include <stdio.h>
main(){
int i, j = 0;
for (i = 0; i < 5; i++){
for(j = 0; j < 12; j++){
printf("%c",'*');
}
printf("%c", '\n'); /*telkens we 12 * gedrukt hebben, springen we naar de volgende regel */
}
}
Dit is dus een for-lus binnen een for-lus. Men spreekt ook van een geneste for-lus. De buitenste lus zorgt ervoor dat de binnenste lus 5 keer herhaald wordt.
Stel nu dat we binnen dit programma meer dan eens een rechthoek willen tekenen. Dan moeten we elke keer die twee for-lussen typen. Het zou handig zijn moesten we dat kunnen omzeilen.
Hier komen functies op het toneel. Een functie is een stukje code dat je kan aanroepen zo veel als je wil. In ons voorbeeld zou het zinvol zijn als we onze functie rechthoek() zouden noemen. De twee haakjes duiden aan dat het om een functie gaat. Een programma met een functie rechthoek(), die tweemaal aangeroepen wordt, ziet er dan zo uit:
#include <stdio.h>
void rechthoek();
main(){
rechthoek();
rechthoek();
}
void rechthoek(){
int i, j = 0;
for (i = 0; i < 5; i++){
for(j = 0; j < 12; j++){
printf("%c",'*');
}
printf("%c", '\n');
}
}
Op de derde lijn wordt een functie gedeclareerd met de naam rechthoek(). Tevens wordt door het woord void ervoor te zetten aangegeven dat deze functie geen waarde aflevert. Sommige functies kunnen een waarde afgeven als resultaat van een bewerking (straks daarover meer). Onze functie rechthoek() levert echter geen waarde af, maar tekent een rechthoek.
Deze lijn declareert enkel een functie, ze zegt nog niets over wat deze functie moet doen. De functiedefinitie volgt na het main()-blok.
Je ziet dat de functiedefinitie ook bestaat uit het aflevertype (void), de naam (rechthoek), twee haakjes en een accolade.
Alles wat tussen deze accolade en de laatste accolade staat, behoort tot deze functie. In de "body" van de functie (dus tussen de accolades) zit de functionaliteit om de rechthoek te tekenen.
Je ziet dat er variabelen gedeclareerd worden binnen de functie. Het is belangrijk te beseffen dat deze variabelen enkel bestaan binnen deze functie. Je kan bijvoorbeeld de waarde van i of j niet laten afdrukken vanuit main(), simpelweg omdat die i en j daar niet bestaan. Je kan trouwens een int i declareren binnen main(), maar dat zal dan een andere variabele zijn dan de i uit de functie rechthoek().
In het main()-blok wordt de functie rechthoek() tweemaal aangeroepen.
Onze functie doet wel wat ze moet doen (rechthoeken tekenen), maar echt flexibel is ze niet. Het zou mooi zijn, mochten we de lengte en breedte bij iedere aanroep kunnen opgeven. Het zou leuk zijn, moesten we kunnen zeggen:
main(){
rechthoek(12, 5);
rechthoek(4, 9);
}
Dit is mogelijk door de functie zo te declareren dat ze parameters aanvaardt. Dit gebeurt als volgt:
void rechthoek(int lengte, int breedte);
Hiermee geven we aan dat we binnen de functie twee variabelen gaan gebruiken, lengte en breedte, die hun waarde krijgen bij de aanroep van de functie. De functiedefinitie zelf ziet er zo uit:
void rechthoek(int lengte, int breedte){
int i, j = 0;
for (i = 0; i < breedte; i++){
for(j = 0; j < lengte; j++){
printf("%c",'*');
}
printf("%c", '\n');
}
}
We kunnen de functie nog flexibeler maken door ook het af te drukken teken op te nemen in de parameterlijst. Het volledige programma ziet er dan zo uit:
#include <stdio.h>
void rechthoek(int lengte, int breedte, char teken);
main(){
rechthoek(12,5, '#');
rechthoek(5,3, 'O');
}
void rechthoek(int lengte, int breedte, char teken){
int i, j = 0;
for (i = 0; i < breedte; i++){
for(j = 0; j < lengte; j++){
printf("%c", teken);
}
printf("%c", '\n');
}
}
De volgorde van de parameters is belangrijk. Je moet een functie steeds aanroepen met de parameters in de volgorde waarin de functie ze verwacht. Dus in dit geval zou de volgende aanroep fout geweest zijn:
rechthoek('#',12, 5);
Als een functie geen parameters krijgt, schrijft men void ook tussen de haken. Dus onze eerste functie rechthoek() had er zo moeten uitzien:
void rechthoek(void);
Deze code zegt dat de parameterlijst leeg is (void). Ook is de afleverwaarde leeg, m.a.w. de functie levert niets af.
Stel dat wij een functie willen schrijven die het gemiddelde aflevert van 2 getallen. Deze functie kunnen we best de naam gemiddelde geven. Het is duidelijk dat de functie twee parameters neemt, nl. de getallen waarvan het gemiddelde moet worden genomen. Het resultaat van die functie is het gemiddelde. Dit is de waarde die dus afgeleverd wordt. Stel dat we twee floats als parameter nemen, dan zal de functie ook een float als gemiddelde afleveren:
float gemiddelde (float getal1, float getal2);
In de declaratie van de functie (men spreekt ook wel van het prototype) zien we reeds dat de functie een float zal afleveren.
De functiedefinitie (ook wel implementatie geheten) ziet er zo uit:
float gemiddelde (float getal1, float getal2){
float resultaat;
resultaat = (getal1 + getal2)/2;
return resultaat;
}
We maken een float resultaat die zal afgeleverd worden door de functie. Dit afleveren gebeurt door het return statement.
Omdat deze functie een float aflevert, mag hij aangeroepen worden waar je een float zou mogen schrijven. Het volledige programma:
#include <stdio.h>
float gemiddelde (float getal1, float getal2);
main(){
float a= 3, b= 4, c = 0;
c = gemiddelde (a, b);
printf( "%f\n", c);
}
float gemiddelde (float getal1, float getal2){
float resultaat;
resultaat = (getal1 + getal2)/2;
return resultaat;
}
Op de tweede regel van main() zie je dat c de waarde krijgt die de functie aflevert. Nu zijn we op het punt gekomen waar we de main() op een juiste manier kunnen declareren: main() is namelijk een functie die een integer aflevert. De juiste declaratie is dus:
int main(){
...
return 0;
}
Omdat main() een int aflevert, MOET je een return opnemen in het main()-blok. Als main() een 0 aflevert aan het besturingssysteem, wil dat zeggen dat het programma foutloos beeïndigd is. Elke andere waarde duidt op een fout.
Het feit dat je een main() ook mag declareren zonder return-waarde, komt doordat dat vroeger niet moest. Men ziet het door de vingers omdat anders alle code die vroeger geschreven is, herschreven zou moeten worden.
Zelfs een functie als printf() levert een int, namelijk het aantal afgedrukte karakters.
Bekijk eens goed het volgende programma:
#include <stdio.h>
void test (int n);
int main(){
int i = 0;
printf("i is gelijk aan %d\n", i);
test (i);
printf("i is nog steeds gelijk aan %d\n", i);
return 0;
}
void test (int n){
n += 1;
printf("Binnen de functie is n nu gelijk aan %d\n", n);
}
Als je dit programma draait, krijg je als output:
i is gelijk aan 0
Binnen de functie is n nu gelijk aan 1
i is nog steeds gelijk aan 0
Dat komt omdat de functie test() niet de variabele i meekrijgt maar de waarde van i. Je zou kunnen zeggen dat er een kopie genomen wordt van i, maar dat i zelf onaantastbaar is voor een functie. De functie test mag vanalles doen met de waarde die ze krijgt via haar parameter, ze kan gewoonweg niet raken aan i zelf. In het Engels heet dit systeem call by value.
In het volgende stukje zullen we zien hoe we een variabele toch kunnen veranderen door een functie-aanroep.