Thema indholdsfortegnelse -- Tastaturgenvej: 'u'  Forrige tema i denne lektion -- Tastaturgenvej: 'p'  Næste slide i denne lektion -- Tastaturgenvej: 'n'Funktioner
12.  Procedurer og funktioner

Vi ser i dette kapitel på procedurer og funktioner generelt, og på funktionsbegrebet i C som dækker begge af disse.

12.1 Procedurer og funktioner12.5 Eksempel: Antal dage i en måned
12.2 Funktionsdefinitioner i C12.6 Eksempel: GCD
12.3 Funktionskald i C12.7 Parametre til C funktioner
12.4 Eksempel: Temperaturomregninger
 

12.1.  Procedurer og funktioner
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Her følger kort vore definitioner af en procedure og en funktion:

En procedure er en abstraktion over en sekvens af kommandoer

En funktion er en abstraktion over et udtryk

Indkapsling er et helt basalt element i både procedurer og funktioner.

Et antal kommandoer sammensættes (aggregeres) i en procedure, og disse kan efterfølgende benyttes som én kommando i et procedurekald. Sammensætningen sker via en blok, som vi så på i kapitel 7.

I en funktion indkapsles ét udtryk, og indkapslingen spiller selv rollen som et udtryk, i et funktionskald.

  • Procedure

    • En proceduredefinition indkapsler et antal kommandoer

    • Kaldet af en procedure er selv en kommando

  • Funktion

    • En funktionsdefinition indkapsler netop ét udtryk

    • Kaldet af en funktion er selv et udtryk

Så vi ser at et procedurekald er en kommando. Som sådan forvents kaldet af en procedure at ændre det kørende programs tilstand. Vi ser også at et funktionskald er et udtryk. Som sådan forventes et funktionskald ikke at ændre på programmets tilstand. Derimod vil et funktionskald give en værdi, som efterfølgende kan bruges på mange forskellige måder (skrive det ud, overføre det til en funktion, overføre det til en procedure, etc.)

Genbrugelighed og generalitet fremmes ved brug af parametre i funktioner. Vi ser konkrete eksempler på dette i afsnit 12.4 og de efterfølgende afsnit. Selve parametermekanismen diskuteres i afsnit 12.7.

 

12.2.  Funktionsdefinitioner i C
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Både procedurer og funktioner, som defineret i afsnit 12.1 kaldes funktioner i C.

C skelner ikke skarpt mellem procedurer og funktioner

I C kaldes begge abstraktionsformer funktioner

Her følger den essentielle syntaks for en funktion i C:


type function_name (formal_parameter_list) {
  declarations
  commands
}
Syntaks 12.1    En funktionsdefinition i C

De formelle parametre er de parametre, som findes i selve funktionsdefinitionen. Når vi senere kalder funktionen angives aktuelle parametre, som erstatter de formelle parametre på en måde, som vi vil se nærmere på i forbindelse med eksemplerne i afsnit 12.4 - afsnit 12.7.

  • Karakteristika:

    • Procedurer angiver type som void

    • Funktioner angiver type som en kendt datatype, f.eks. int, char, eller double

    • I procedurer og funktioner uden parametre angives formal_parameter_list som void

Mange C funktioner er reelt procedurer, som returnerer en eller anden værdi. Den returnerede værdi anvendes ofte til at signalere, om de indkapslede kommandoer 'gik godt' eller 'gik skidt'. I denne sammenhæng betyder returværdien 0 ofte, at alt er OK, medens en ikke-nulværdi som resultat er tegn på en eller anden fejl.

 

12.3.  Funktionskald i C
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Når man kalder en funktion aktiveres kommandoerne i kroppen af funktionen på det vi kalder de aktuelle parametre. Et kald af funktionen står altså for de indkapslede kommandoer, hvor de aktuelle parametres værdier erstatter de formelle parametre. Dette kalder vi parameteroverførsel, og vi beskrive dette nærmere i afsnit 12.7.


function_name (actual_parameter_list)
Syntaks 12.2    Et funktionskald i C

  • Karakteristika:

    • En funktion kan ikke kaldes før den er erklæret

    • Antallet af aktuelle parametre i kaldet skal modsvare antallet af formelle parametre i definitionen

    • En aktuel parameter er et udtryk, som beregnes inden den overføres og bindes til den tilsvarende formelle parameter

 

12.4.  Eksempel: Temperaturomregninger
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Vi ser først på et eksempel på en ægte funktion. Hermed mener vi altså en funktion i den forstand vi definerede i afsnit 12.1, og som modsvarer det matematiske funktionsbegreb.

Funktionen fahrenheit_temperature tager som input en celcius temperatur, f.eks. 100 grader (kogepunktet ved normale forhold). Med dette output returnerer funktionen den tilsvarende Fahrenheit temperatur, 212 grader. Dette er den normale temperaturangivelse i Amerikanske lande, f.eks. USA.

Der udføres ingen kommandoer i C funktionen fahrenheit_temperature. Funktionen dækker med andre ord blot over en formel. Vi kalder ofte en formel for et udtryk, jf. afsnit 2.1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

double fahrenheit_temperature(double celcius_temp){
  return (9.0 / 5.0) * celcius_temp + 32.0;
}

int main(void){

  printf("Freezing point: %6.2lf F.\n", 
         fahrenheit_temperature(0.0));

  printf("Boiling point: %6.2lf F.\n",
         fahrenheit_temperature(100.0));
  
  return 0;
}
Program 12.1    Et program som omregner frysepunkt og kogepunkt til Fahrenheit grader.

I programmet ser vi øverst, med fed sort skrift definitionen af fahrenheit_temperature. Bemærk udtrykket (9.0 / 5.0) * celcius_temp + 32.0 , hvis værdi returneres fra funktionen med return. Bemærk også anvendelsen af navnet celcius_temp; Dette er den formelle parameter af funktionen.

I main ser vi to kald af fahrenheit_temperature. Dette er de røde og blå dele af program 12.1. Bemærk at disse dele er udtryk, og at de forekommer som parametre til printf.

Herunder, i figur 12.1 illustrerer vi det røde og blå kald af fahrenheit_temperature i program 12.1.

Kontrollen i main forløber som følger (følg pilene):

  1. Indgang til main (lodret sort pil).
  2. Det røde kald af fahrenheit_temperature (vandret rød pil til højre).
  3. Første udførelse af fahrenheit_temperature (lodret sort pil).
  4. Den røde returnering fra fahrenheit_temperature (opadrettet rød venstre pil).
  5. Det blå kald af fahrenheit_temperature (vandret blå pil til højre).
  6. Anden udførelse af fahrenheit_temperature (lodret sort pil).
  7. Den blå returnering fra fahrenheit_temperature (blå pil mod venstre).
  8. Udgang af main (lodret sort pil).

Figur 12.1    To kald af fahrenheit_temperature funktionen

Herunder, i program 12.2 viser vi et eksempel på brug af den omvendte funktion, som konverterer fahrenheit grader til celcius grader. Konkret programmerer vi en nyttig temperatur konverteringstabel. De første linier i udskriften er vist i program 12.3.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

double celcius_temperature(double fahrenheit_temp){
  return (5.0 / 9.0) * (fahrenheit_temp - 32.0);
}

int main(void){
  double f;  

  printf("%-20s %-20s\n", "Fahrenheit degrees", "Celcius degrees");

  for (f = 1.0; f <= 100.0; f++)
    printf("%-20.2lf %-20.2lf\n", f, celcius_temperature(f));
  
  return 0;
}
Program 12.2    Et program som udskriver en nyttig temperaturkonverteringstabel.

Bemærk kontrolstrengen i printf i program 12.2, såsom %-20.2lf. lf benyttes idet vi udskriver en double. 20.2 angiver antallet af cifre og antallet af cifre efter decimal point. Endelig angiver tegnet - and vi ønsker venstrestilling af output i feltet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
1.00                 -17.22              
2.00                 -16.67              
3.00                 -16.11              
4.00                 -15.56              
5.00                 -15.00              
6.00                 -14.44              
7.00                 -13.89              
8.00                 -13.33              
9.00                 -12.78              
10.00                -12.22              
11.00                -11.67              
12.00                -11.11              
13.00                -10.56              
14.00                -10.00              
15.00                -9.44               
16.00                -8.89               
17.00                -8.33               
18.00                -7.78               
19.00                -7.22               
20.00                -6.67               
21.00                -6.11               
22.00                -5.56               
23.00                -5.00               
24.00                -4.44               
25.00                -3.89               
26.00                -3.33               
27.00                -2.78               
28.00                -2.22               
29.00                -1.67               
30.00                -1.11               
31.00                -0.56               
32.00                0.00                
33.00                0.56  
...                  ...
Program 12.3    Partiel output fra ovenstående program.

 

12.5.  Eksempel: Antal dage i en måned
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Eksemplet i dette afsnit er en fortsættelse af eksemplet fra program 8.4. Bidraget i dette afsnit er en pæn indpakning af de relevante programdele i en funktion, og en lettere omskrivning, som går på at vi nu tager udgangspunkt i et månedsnummer og et årstal.

Bemærk lige kontrasten mellem program 8.4 og program 12.4. I det første program var vi i stand til at beregne antallet af dage i en måned som en del af main. I det sidste program har vi en veldefineret byggeklods, funktionen daysInMonth, som kan bruges så ofte vi vil. Dette er en meget mere modulær løsning. En løsning der på alle måder er mere tilfredsstillende.

Vi introducerer en funktion i programmet der beregner antal dage i en måned

Vi bruger eksemplet til at diskutere overførsel af parametre til funktioner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <stdlib.h>

int daysInMonth(int mth, int yr);
int isLeapYear(int yr);

int main(void) {

  int mth, yr;

  do{
    printf("Enter a month - a number between 1 and 12: ");
    scanf("%d", &mth);
    printf("Enter a year: ");
    scanf("%d", &yr);

    printf("There are %d days in month %d in year %d\n",
              daysInMonth(mth, yr), mth, yr);
  } while (yr != 0);

  return 0;
}

int daysInMonth(int month, int year){
  int numberOfDays;
  switch(month){
    case 1: case 3: case 5: case 7: case 8: case 10: case 12: 
      numberOfDays = 31; break;
    case 4: case 6: case 9: case 11: 
      numberOfDays = 30; break;
    case 2:
      if (isLeapYear(year)) numberOfDays = 29; 
      else numberOfDays = 28; break;
    default: exit(-1);  break;
  }
  return numberOfDays;
}   

int isLeapYear(int year){
  int res;
  if (year % 400 == 0)
    res = 1;
  else if (year % 100 == 0)
    res = 0;
  else if (year % 4 == 0)
    res = 1;
  else res = 0;
  return res;
}
Program 12.4    Et program der beregner antal dage i en måned med en funktion.

I program 12.4 bemærker vi først, at vi har prototyper af funktionerne daysInMonth og isLeapYear (fremhævet med fed sort skrift). Dernæst følger main, som kalder daysInMonth (den blå del). Endelig følger definitionen af daysInMonth med rød skrift. Som input tager funktionen parametrene month og year, og som output returneres antallet af dage i den ønskede måned. Læg mærke til denne meget veldefinerede grænseflade mellem funktionen daysInMonth og dets omverden.

Vi har øget genbrugeligheden - og dermed værdien af vort program

Vi ønsker som regel at undgå brug af scanf og printf i genbrugelige procedurer og funktioner

Lad mig udddybe ønsket om at undgå brug af scanf og printf i genbrugelige procedurer og funktioner. Hvis vi foretager indlæsning af data og/eller udskrivning af resultater i vore genbrugelige procedurer og funktioner binder vi procedurerne og funktionerne til bestemt brugsmønstre og brugssammenhænge. Som et eksempel vil det fremgå om vi fører dialogen med brugeren på engelsk eller dansk. Det er derfor bedst at isolere input og output til bestemte dele af programmet, f.eks. til main eller til et lag af funktioner omkring main. Resten af programmet bør skrives uden brug af input og output procedurer.

 

12.6.  Eksempel: GCD
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Vi vender tilbage til et andet eksempel fra kapitel 9, nemlig Euclids algoritme, som vi oprindelig mødte i program 9.1. Vi husker at Euclids algoritme finder den største fælles divisor af to tal. Altså det største tal, som både går op i det ene og det andet tal.

Mønstret fra dage-i-måned eksemplet, program 12.4, genfindes klart i program 12.5. Læg mærke til funktionen gcd's grænseflader. På inputsiden er der helt naturligt to heltal, hvoraf vi ønsker at finde den største fælles divisor. På outputsiden resultatet, altså den største fælles divisor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>

int gcd(int, int);

int main(void) {
  int i, j, small, large;
 
  printf("Enter two positive integers: ");
  scanf("%d %d", &i, &j);

  small = i <= j ? i : j;
  large = i <= j ? j : i;
  
  printf("GCD of %d and %d is %d\n\n", i, j, gcd(large, small));
  
  return 0;
}

int gcd(int large, int small){
  int remainder; 
  while (small > 0){
    remainder = large % small;
    large = small;
    small = remainder;
  }
  return large;
}   
Program 12.5    Et program der beregner største fælles divisor med en funktion.

 

12.7.  Parametre til C funktioner
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Som afslutning på dette afsnit ser vi lidt mere systematisk på parametre til funktioner i C.

Vi har mødt eksempler på parametre i både program 12.1, program 12.4 og program 12.5. I dette afsnit ser vi på den faktiske mekanisme til parameteroverførsel i C. Dette er de såkaldte værdiparametre, som på engelsk ofte betegnes som "call by value".

Parametre til en funktion bruges til at gøre funktionen mere generelt anvendelig

En parameter, som optræder i en funktionsdefinition, kaldes en formel parameter. En formel parameter er et navn.

En parameter, som optræder i et kald af en funktion, kaldes en aktuel parameter. En aktuel parameter er et udtryk, som beregnes inden det overføres.

  • Regler for parameteroverførsel:

    • Antallet af aktuelle parametre i kaldet skal modsvare antallet af formelle parametre i definitionen

    • Typen af en aktuel parameter skal modsvare den angivne type af hver formel parameter

      • Dog muligheder for visse implicitte typekonverteringer

    • Parametre til funktioner i C overføres som værdiparametre

      • Der overføres en kopi af den aktuelle parameters værdi

      • Kopien bindes til (assignes til) det formelle parameternavn

      • Når funktionen returnerer ophører eksistensen af de formelle parametre, på samme måde som lokale variable i funktionen.

Genereret: Onsdag 7. Juli 2010, 15:10:47
Thema indholdsfortegnelse -- Tastaturgenvej: 'u'  Forrige tema i denne lektion -- Tastaturgenvej: 'p'  Næste slide i denne lektion -- Tastaturgenvej: 'n'