Kapitel 6
Mere om Funktioner

Kurt Nørmark
Institut for Datalogi, Aalborg Universitet


Sammendrag
Forrige lektion Næste lektion
Stikord Referencer Indhold
I denne lektion gennemgår vi funktions- og procedure begrebet, herunder lokale variable og parametre. Vi ser naturligvis også på detaljer omkring funktioner i C.


C funktioner med output parametre

Output fra funktioner
Slide Indhold Stikord
Referencer 

Funktionens grænseflade til resten af programmet udgøres af parametrene

Dette gælder input såvel som output

Output parametre kan i C håndteres med brug af pointere - call-by-reference

  • Output fra en funktion:

    • Via funktionens returværdi - return something;

      • En funktions-orienteret løsning

      • Problematisk hvis der er mere end ét output

    • Via indkaspling af flere stykker output i én helhed som kan returneres

      • Bliver muligt når vi introducerer struct senere i kurset

    • Via et antal parametre af pointer typer

      • En imperativ løsning - emnet i denne lektion

      • Call-by-reference parametre

Single eller multiple return
Slide Indhold Stikord
Referencer 

Begrebet single return: En funktion har single return hvis der kun findes én return sætning i slutningen af funktionen
Begrebet multiple return: En funktion har multiple return hvis der findes to eller flere return sætninger spredt i funktionen

Program: Funktionen daysInMonth - med single return.
int daysInMonth(int month, int year){
  int numberOfDays = ERROR_VALUE;
  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;
  }
  return numberOfDays;
}   

Program: Funktionen daysInMonth - med multiple return.
int daysInMonth(int month, int year){
  switch(month){
    case 1: case 3: case 5: case 7: case 8: case 10: case 12: 
      return 31; 
    case 4: case 6: case 9: case 11: 
      return 30; 
    case 2:
      if (isLeapYear(year))
        return 29; 
      else
        return 28;
    default:
      return ERROR_VALUE;
  }
}   

Det er ofte mest gennemskueligt - og mest sikkert - at have én single return i bunden af hver funktion

I nogle situationer er multiple return dog retfærdiggjort

Introduktion til Pointere (1)
Slide Indhold Stikord
Referencer 

Normalt tilgås pladser i lageret ved brug af faste navne

Alternativt kan pladser i lageret tilgås via adresser, som kaldes pointere

Figur. En variabel v og en pointer til v

Introduktion til Pointere (2)
Slide Indhold Stikord
Referencer 

Fortsættelse af eksemplet

Figur. En variabel v og en pointer til v

Program: Et tilsvarende C program - lettere udvidet.
#include <stdio.h>

int main(void) {
  
  int v = 7;
  int *pv = &v;

  *pv = 8;

  printf("v = %d\n", v);
  printf("pv points at %d\n", *pv);
  printf("pv is the pointer %x\n", pv);

  return 0;
}

Program: Program output.
v = 8
pv points at 8
pv is the pointer 28ff18

To operatorer knyttet til pointere
Slide Indhold Stikord
Referencer 

Adresse operatoren &

Dereferencing operatoren *

  • Address operator

    • Udtrykket &v returnerer adressen hvori variablen v er lagret

    • Hvis typen af v er t, er typen af &v t*

    • t*   læses som   en pointer til t

  • Dereferencing operator - eller indirection operator

    • Udtrykket *pv returnerer den værdi, som pv peger på.

    • *pv følger pointeren i pv

    • *pv kan både optræde på venstre og højre side af et assignment

Henvisning

Både adresse operatoren og dereferencing operatoren er unære, præfix operatorer

Begge operatorer & og * har også en anden betydning - hhv. bitvis and og multiplikation

Output parametre i scanf
Slide Indhold Stikord
Referencer 

Vi kan nu endelig forstå hvordan scanf virker

I særdeleshed hvorfor adresse operatoren & er så vigtig i kald af scanf

Program: .
#include <stdio.h>

int main(void) {
  double d;
  int i;

  printf("Please enter a double followed by an integer:\n");
  scanf("%lf %d", &d, &i);

  printf("d: %f, i: %d\n", d, i);
    
  return 0;
}

Der overføres to pointere til scanf.

Disse pointere udpeger to pladser i lageret (variable), hvor scanf kan placere de indlæste værdier.

Output parametre - ift. input parametre
Slide Indhold Stikord
Referencer 

Input parametre sammenlignet med output parametre

Figur. Værdi parametre (til input) og reference parametre (til output)

Program: Funktionen f modtager værdierne af to variable som input - figur til venstre.
#include <stdio.h>

/* Input: We pass the values of two variables to f */
 
void f(int fp1, int fp2){
  printf("fp1 + fp2 = %d + %d = %d\n", fp1, fp2, fp1 + fp2);
}

int main(void) {
  int i = 3,
      j = 4;
  
  f(i, j);

  printf("i + j = %d + %d = %d\n", i, j, i + j);
  
  return 0;
}

Program: Samme som ovenfor - sammenfaldende navnet af formelle og aktuelle parametre - intet ændret.
#include <stdio.h>

/* Input: The same as the previous program. The formal and actual parameters have the same names. */
 
void f(int i, int j){
  printf("i + i = %d + %d = %d\n", i, j, i + j);
}

int main(void) {
  int i = 3,
      j = 4;
  
  f(i, j);

  printf("i + j = %d + %d = %d\n", i, j, i + j);
  
  return 0;
}

Program: Program output.
fp1 + fp2 = 3 + 4 = 7
i + j = 3 + 4 = 7

Program: Samme som ovenfor - men vi forsøger at bruge i og j fra main i f.
#include <stdio.h>

/* Input: Same as above, but we attempt to use i and j from main in f. The compiler spots the error. */
 
void f(int fp1, int fp2){
  printf("fp1 + fp2 = %d + %d = %d\n", fp1, fp2, i + j);      /* i and j are undeclared */
}

int main(void) {
  int i = 3,
      j = 4;
  
  f(i, j);

  printf("i + j = %d + %d = %d\n", i, j, i + j);
  
  return 0;
}

Program: Funktionen f modtager værdierne af to udtryk som input.
#include <stdio.h>

/* Input: We pass the values of two expressions to f */
 
void f(int fp1, int fp2){
  printf("fp1 + fp2 = %d + %d = %d\n", fp1, fp2, fp1 + fp2);
}

int main(void) {
  int i = 3,
      j = 4;
  
  f(i + 1, j + 2);

  printf("i + j = %d + %d = %d\n", i, j, i + j);
  
  return 0;
}

Program: Program output.
fp1 + fp2 = 4 + 6 = 10
i + j = 3 + 4 = 7

Program: Funktionen f ændrer på værdierne af de to formelle parametre.
#include <stdio.h>

/* Input: We pass the values of two variables to f.
   We change the values of the formal parameters in f. */
 
void f(int fp1, int fp2){
  fp1 += 1; fp2 += 2;                       /* Bad to alter the values of formal parameters */
  printf("fp1 + fp2 = %d + %d = %d\n", fp1, fp2, fp1 + fp2);
}

int main(void) {
  int i = 3,
      j = 4;
  
  f(i, j);

  printf("i + j = %d + %d = %d\n", i, j, i + j);
  
  return 0;
}

Program: Program output.
fp1 + fp2 = 4 + 6 = 10
i + j = 3 + 4 = 7

Program: Funktionen f ændrer på værdierne af de to lokale variable.
#include <stdio.h>

/* Input: We pass the values of two variables to f, and use these for initialization of two local variables.
   We change the values of the local variables in f. */
 
void f(int fp1, int fp2){
  int a = fp1, b = fp2;                /* We initialize local varibale from formal parameters */
  a += 1; b += 2;                      /* OK to assign to local variabels */
  printf("a + b = %d + %d = %d\n", a, b, a + b);
}

int main(void) {
  int i = 3,
      j = 4;
  
  f(i, j);

  printf("i + j = %d + %d = %d\n", i, j, i + j);
  
  return 0;
}

Program: En funktion g der modtager to pointere til variable som input - med henblik på tilbageføring af output - figur til højre.
#include <stdio.h>

/* Output: We pass pointers as parameters to g*/

void g(int *fp1, int *fp2){
  *fp1 = *fp1 + 1;
  *fp2 = 6;    
  printf("*fp1 + *fp2 = %d + %d = %d\n", *fp1, *fp2, *fp1 + *fp2);
}

int main(void) {

  int i = 3,
      j = 4;
  
  g(&i, &j);  
  printf("i + j = %d + %d = %d\n", i, j, i + j);
  
  return 0;
}

Program: Program output.
*fp1 + *fp2 = 4 + 6 = 10
i + j = 4 + 6 = 10

Program: En funktion g der modtager to pointere til udtryk som input - Giver ikke mening.
#include <stdio.h>

/* Output: We attempt to pass two pointers to g - not successful - the compiler finds out */

void g(int *fp1, int *fp2){
  *fp1 = *fp1 + 1;
  *fp2 = 6;    
  printf("*fp1 + *fp2 = %d + %d = %d\n", *fp1, *fp2, *fp1 + *fp2);
}

int main(void) {

  int i = 3,
      j = 4;
  
  g(&(i + 1), &(j + 2));                            /* Invalid lvalues */
  printf("i + j = %d + %d = %d\n", i, j, i + j);  
  
  return 0;
}

Program: Der udføres pointer aritmetik på pointerne - usikkert og uden god mening.
#include <stdio.h>

/* Output: We attempt to pass two pointers to g - but the pointers are not good... 
   Compiles. May or may not run. */

void g(int *fp1, int *fp2){
  *fp1 = *fp1 + 1;
  *fp2 = 6;    
  printf("*fp1 + *fp2 = %d + %d = %d\n", *fp1, *fp2, *fp1 + *fp2);
}

int main(void) {

  int i = 3,
      j = 4;
  
  g(&i + 1, &j + 2);                      /* Now Lvalues, but unsafe! */
  printf("i + j = %d + %d = %d\n", i, j, i + j);  
  
  return 0;
}

Program: Program output.
*fp1 + *fp2 = 6 + 6 = 12
i + j = 3 + 4 = 7

Program: Tilbageføring af output fra g med scanf.
#include <stdio.h>

/* Output: We pass pointers two variables to g. 
   Scanf receives these pointers. Scanf can hereby affect variables in main. */

void g(int *fp1, int *fp2){
  printf("Enter two integers: ");
  scanf("%d %d", fp1, fp2);
}

int main(void) {

  int i, j;   /* Not initialized */
  
  g(&i, &j);  
  printf("i + j = %d + %d = %d\n", i, j, i + j);
  
  return 0;
}

Program: Program input og output.
Enter two integers: 3 4
i + j = 3 + 4 = 7

Eksempel: Rødder i en andengradsligning
Slide Indhold Stikord
Referencer 

Koeficienterne til polynomiet i andengradsligningen er input parametre til funktionen

Antallet af rødder - og rødderne selv - er output parametre til funktionen

Henvisning

Program: En funktion der finder rødder i en andengradsligning - både input og output parametre.
#include <stdio.h>
#include <math.h>


/* Find roots in the quadratic equation a * x*x + b * x + c = 0.
   Assume as a precondition that a is not zero                    */
void solveQuadraticEquation(double a, double b, double c, 
                            int *numberOfRoots, double *root1, double *root2){
  double discriminant;

  discriminant = b * b - 4 * a * c;

  if (discriminant < 0){
    *numberOfRoots = 0;
  }
  else if (discriminant == 0){
    *numberOfRoots = 1;
    *root1 = -b/(2*a);
  }
  else{
    *numberOfRoots = 2;
    *root1 = (-b + sqrt(discriminant))/(2*a);
    *root2 = (-b - sqrt(discriminant))/(2*a);
  }
}   

int main(void) {
  double a, b, c, firstRoot, secondRoot;
  int numberOfRoots;
  printf("Enter coeficients a, b, and c: ");
  scanf("%lf %lf %lf", &a, &b, &c);

  if (a != 0){
    solveQuadraticEquation(a, b, c, &numberOfRoots, &firstRoot, &secondRoot);  
    if (numberOfRoots == 0)
      printf("No roots\n");
    else if (numberOfRoots == 1)
      printf("One root: %f\n", firstRoot);
    else 
      printf("Two roots: %f and %f\n", firstRoot, secondRoot);
  }
  else
    printf("The coeficient a must be non-zero");

  return 0;
}

  • Output parametre i eksemplet - call-by-reference parametre:

    • Der overføres en pointer til en int og two pointere til doubles

    • Output fra rod-beregningen formidles tilbage til kaldstedet gennem disse tre pointere

Denne udgave af funktionen er langt bedre end udgaven der blot printer rødderne!

Opgave 6.4. Celcius til fahrenheit med output parameter

Vi har tidligere programmeret en simpel funktion, der omregner fra en celcius temperatur til fahrenheit temperatur. Det er helt naturligt at celcius temperaturen er en call by value input parameter. Ligeledes er det naturligt at fahrenheit temperaturen returneres med return fra funktionen.

Omskriv nu funktionen således at fahrenheit temperaturen returneres gennem en output parameter - en pointer. Omskriv også main, således at kaldet ændres til denne nye parameterprofil.

Hvilken version foretrækker du?

Opgave 6.4. Timer, minutter og sekunder - igen, igen

Vi har på et tidligt tidspunkt i kurset skrevet et program, som omregner et antal sekunder til timer, minutter og sekunder efter de sædvanlige principper.

Skriv nu en funktion, hours_minutes_seconds, som tager antal af sekunder som en input parameter, og som returnerer de tre outputs (timer, minutter og sekunder) som output parametre (pointere, call-by-reference).

Opgave 6.4. Seddeludlevering i pengeautomat

Dette program handler om seddeludlevering fra en amerikansk pengeautomat, hvor der kun anvendes 100, 50, 20 og 10 dollar sedler.

Målet med programmet er at arbejde med både input og outputparametre af en funktion.

Programmet skal som input acceptere et dollar beløb, der er dividerbart med 10. Programmet skal beregne antallet af udleverede 100, 50, 20 og 10 dollar sedler svarende til beløbet. Der skal udleveres så få sedler som muligt.

Problemet skal løses med en funktion der tager både input og output parametre. Beløbet, der skal veksles, skal være en input parameter. Antallet af udleverede 100, 50, 20 og 10 dollar sedler skal være output parametre.

Pointere og Konstanter
Slide Indhold Stikord
Referencer 

C understøtter pointere til konstanter, konstante pointere og konstante pointere til konstanter

  • const T *c

    • En pointer c der peger på en konstant af type T

    • Det der peges på kan ikke ændres

  • T *const d

    • En konstant pointer der peger på en variable af type T

    • Pointeren kan ikke ændres

  • const T *const e

    • En konstant pointer der peger på en konstant af type T

    • Hverken konstanten eller det den peger på kan ændres

Program: Ekspempler på forkellige former for konstanter og pointere.
int main(void) {

  int a = 1;                /* a is an integer */
  const int b = 1;          /* b is a constant integer */

  const int *c = &b;        /* c is pointer to a constant integer */  
 
  int *const d = &a;        /* d is a constant pointer to an integer */   

  const int *const e = &a;  /* d is a constant pointer to a constant integer */   

  c = &a;                   /* OK - the pointer in c is not constant */
  *c = 2;                   /* NOT OK */

  d = &b;                   /* NOT OK. d is a constant pointer */
  *d = 3;                   /* OK - the integer pointed to is not constant */

  e = &b;                   /* NOT OK. e is a constant pointer */
  *e = 4;                   /* NOT OK. *e is a constant integer */

  return 0;
}


Rekursive funktioner

Introduktion til rekursive funktioner
Slide Indhold Stikord
Referencer 

Rekursive funktioner er nyttige når et problem kan opdeles i delproblemer, hvoraf nogle har samme natur som problemet selv

En rekursiv funktion kalder sig selv

Henvisning

Program: Et program med en rekursivt defineret fakultetsfunktion.
#include <stdio.h>

unsigned long int factorial(unsigned int n){
  if (n == 0)
    return 1;
  else 
    return n * factorial(n - 1);
}

int main(void) {
  unsigned int k;

  /* Upper limits of k:  12  (if long is 4 bytes) */
  /* Upper limits of k:  20  (if long is 8 bytes) */

  for (k = 1; k <= 12; k++)                     
    printf("%-20u %20lu\n", k, factorial(k));
  
  return 0;
}

Program: Et tilsvarende program med en iterativ fakultetsfunktion.
#include <stdio.h>

unsigned long int factorial(unsigned int n){
  unsigned int i; 
  unsigned long result = 1;
  
  for(i = 1; i <= n; i++)
    result *= i;

  return result;
}

int main(void) {
  unsigned int k;

  for (k = 1; k <= 12; k++)
    printf("%-20u %20lu\n", k, factorial(k));
  
  return 0;
}

Program: Output fra ovenstående programmer.
1                                       1
2                                       2
3                                       6
4                                      24
5                                     120
6                                     720
7                                    5040
8                                   40320
9                                  362880
10                                3628800
11                               39916800
12                              479001600

Henvisning

Program: En rekursiv version af funktionen findRootBetween.
double findRootBetween(double l, double u){
  if (isSmallNumber(f(middleOf(l,u))))
     return middleOf(l,u);
  else if (sameSign(f(middleOf(l,u)), f(u)))
     return findRootBetween(l, middleOf(l,u));
  else if (!sameSign(f(middleOf(l,u)), f(u)))
     return findRootBetween(middleOf(l,u),u);
  else exit(-1);
}  

Program: Den iterative version af funktionen findRootBetween - for reference .
double findRootBetween(double a, double b){
  double l = a, u = b;
  while (!isSmallNumber(f(middleOf(l,u)))){ 
    if(sameSign(f(middleOf(l,u)), f(u)))
      u = middleOf(l,u);
    else 
      l = middleOf(l,u);
  }
  return middleOf(l,u);
}  

Program: Hele rodsøgningsprogrammet.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
 
double f (double x){
  /* (x - 5.0) * (x - 3.0) * (x + 7.0) */
  return (x*x*x - x*x - 41.0 * x + 105.0);
}

int sameSign(double x, double y){
  return x * y >= 0.0;
}

double middleOf(double x, double y){
  return x + (y - x)/2;
}

int isSmallNumber(double x){
  return (fabs(x) < 0.0000001);
}   

double findRootBetween(double l, double u){
  if (isSmallNumber(f(middleOf(l,u))))
     return middleOf(l,u);
  else if (sameSign(f(middleOf(l,u)), f(u)))
     return findRootBetween(l, middleOf(l,u));
  else if (!sameSign(f(middleOf(l,u)), f(u)))
     return findRootBetween(middleOf(l,u),u);
  else exit(-1);
}  

int main (void){
    double x, y;
    int numbers; 

    printf("RECURSIVE ROOT FINDING\n\n");

    do{
      printf("%s","Find a ROOT between which numbers: ");
      numbers = scanf("%lf%lf", &x, &y);

      if (numbers == 2 && !sameSign(f(x),f(y))){
          double solution = findRootBetween(x,y);
          printf("\nThere is a root in %lf\n", solution);
        }
      else if (numbers == 2 && sameSign(f(x),f(y)))
          printf("\nf must have different signs in %lf and %lf\n",
                  x, y);
      else if (numbers != 2)
          printf("\nBye\n\n");
    }
    while (numbers == 2);

    return 0;
}

I hvert rekursivt kald afsættes der plads til nye parametre og nye lokale variable.

Vi vender tilbage til rekursion i en senere lektion


Pointere til funktioner

Pointere til funktioner
Slide Indhold Stikord
Referencer 

Det er ofte nyttigt at overføre funktioner som parametre til andre funktioner

I C er dette muligt ved brug af pointere til funktioner

Program: Funktionen combine som modtager en funktion som parameter.
#include <stdio.h>

double max(double, double);
double min(double, double);
double plus(double, double);
double minus(double, double);

double combine(double a, double b, double c, double d, 
                    double (*combiner)(double, double)){
  return combiner(a, combiner(b, combiner (c, d)));
}

int main(void) {
  double result;

  result = combine(5, 7, 8, 11, &minus);  /* minus(5, minus(7, minus(8, 11))) = 5 - (7 - (8 - 11)) = -5 */
  printf("Minus combination result: %f\n", result);

  result = combine(5, 7, 8, 11, &plus); 
  printf("Plus combination result: %f\n", result);

  result = combine(5, 7, 8, 11, &min); 
  printf("Min combination result: %f\n", result);

  result = combine(5, 7, 8, 11, &max); 
  printf("Max combination result: %f\n", result);

  return 0;
}

double max (double a, double b){
  return a > b ? a : b;
}

double min (double a, double b){
  return a > b ? b : a;
}

double plus(double a, double b){
  return a + b;
}

double minus(double a, double b){
  return a - b;
}

Henvisning

Program: Rodsøgning i en funktionen, som overføres som parameter til findRootBetween.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
 
double pol1 (double x){
  /* (x - 5.0) * (x - 3.0) * (x + 7.0) */
  return (x*x*x - x*x - 41.0 * x + 105.0);
}

double pol2 (double x){
  return (x*x - 2.0);
}


int sameSign(double x, double y){
  return (x > 0 && y > 0) || (x < 0 && y < 0);
}

double middleOf(double x, double y){
  return x + (y - x)/2;
}

int isSmallNumber(double x){
  return (fabs(x) < 0.0000001);
}   

/* Precondition: The sign of f(l) and f(u) are different */
double findRootBetween(double (*f)(double), double l, double u){
 while (!isSmallNumber((*f)(middleOf(l,u)))){ 
   if(sameSign((*f)(middleOf(l,u)), (*f)(u)))
     u = middleOf(l,u);
   else 
     l = middleOf(l,u);
 }
 return middleOf(l,u);
}  

int main (void){
  double x, y;
  int numbers; 
  int function_selection;
  double (*fn)(double);  // fn is a pointer to a function:   double -> double
  int done = 0;

  do{
    /* Select function: */
    printf("Select function:\n");
    printf("1: x*x*x - x*x - 41.0 * x + 105.0\n");
    printf("2: x*x - 2.0\n");
    printf("3: sin\n");
    printf("4: EXIT\n");
    scanf("%d", &function_selection);
    switch(function_selection){
       case 1:
         fn = &pol1; break;
       case 2:
         fn = &pol2; break;
       case 3:
         fn = &sin; break;
       default:
         done = 1;
    }

    /* Find a root in the selected function: */
    if (!done){
      printf("%s","Find a ROOT between which numbers: ");
      numbers = scanf("%lf%lf", &x, &y);

      if (numbers == 2 && !sameSign((*fn)(x),(*fn)(y))){
         double solution = findRootBetween(fn , x ,y);
         printf("\nThere is a root in %lf\n\n", solution);
      }
      else if (numbers == 2 && sameSign((*fn)(x),(*fn)(y)))
         printf("\nf must have different signs in %lf and %lf\n\n",
                  x, y);
      else if (numbers != 2)
         done = 1;
    }
  }
  while (!done);
  return 0;
}

Program: En version uden dereferencing og address operator på funktionerne.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
 
double pol1 (double x){
  /* (x - 5.0) * (x - 3.0) * (x + 7.0) */
  return (x*x*x - x*x - 41.0 * x + 105.0);
}

double pol2 (double x){
  return (x*x - 2.0);
}


int sameSign(double x, double y){
  return (x > 0 && y > 0) || (x < 0 && y < 0);
}

double middleOf(double x, double y){
  return x + (y - x)/2;
}

int isSmallNumber(double x){
  return (fabs(x) < 0.0000001);
}   

/* Precondition: The sign of f(l) and f(u) are different */
double findRootBetween(double (*f)(double), double l, double u){
 while (!isSmallNumber((*f)(middleOf(l,u)))){ 
   if(sameSign((*f)(middleOf(l,u)), (*f)(u)))
     u = middleOf(l,u);
   else 
     l = middleOf(l,u);
 }
 return middleOf(l,u);
}  

int main (void){
  double x, y;
  int numbers; 
  int function_selection;
  double (*fn)(double);  //fn is a pointer to a function:   double -> double
  int done = 0;

  do{
    /* Select function: */
    printf("Select function:\n");
    printf("1: x*x*x - x*x - 41.0 * x + 105.0\n");
    printf("2: x*x - 2.0\n");
    printf("3: sin\n");
    printf("4: EXIT\n");
    scanf("%d", &function_selection);
    switch(function_selection){
      case 1:
        fn = pol1; break;
      case 2:
        fn = pol2; break;
      case 3:
        fn = sin; break;
      default:
        done = 1;
    }

    /* Find a root in selected function: */
    if (!done){
      printf("%s","Find a ROOT between which numbers: ");
      numbers = scanf("%lf%lf", &x, &y);

      if (numbers == 2 && !sameSign(fn(x),fn(y))){
         double solution = findRootBetween( fn , x ,y);
         printf("\nThere is a root in %lf\n\n", solution);
      }
      else if (numbers == 2 && sameSign(fn(x),fn(y)))
         printf("\nf must have different signs in %lf and %lf\n\n",
                x, y);
      else if (numbers != 2)
         done = 1;
    }
  }
  while (!done);
  return 0;
}

Henvisninger

Pointere til funktioner anvendes bl.a. i sorteringsfunktionen qsort fra stdlib.h

Opgave 6.5. En funktion som kalder en anden funktion flere gange

Skriv en funktion multi_apply med tre parametre:

  1. En funktion f fra double til double. f er en pointer til en funktion.
  2. Et heltal n der angiver hvor mange gange f skal kaldes.
  3. En double s, som angiver den værdi som f bliver kaldt på første gang (i det 'inderste' kald).

multi_apply(f, n, s) skal beregne og returnere f(f( ... f(s))), hvor f kaldes n gange.

Eksempelvis - og mere konkret - skal

  multi_apply(f, 4, 16.0)

beregne og returnere f(f(f(f(16.0)))).

Afprøv f.eks. dit program hvor den aktuelle første parameter til multi_apply er

  double half(double x){
    return x/2;
  }

på følgende måde

  multi_apply(half, 4, 16.0)

Hvilken værdi forventer du at se fra dette kald?

Som en variant af ovenstående kan du overveje at programmere multi_apply_2 med følgende fire parametre:

  1. En funktion f fra double til double
  2. En funktion g fra double til double
  3. Et heltal n der angiver hvor mange gange f og g skal kaldes
  4. En double s, som angiver den værdi som g bliver kaldt på første gang (i det 'inderste' kald).

multi_apply_2(f, g, n, s) skal beregne og returnere (f (g (f (g ... (f (g s)))))), hvor både f og g kaldes n gange.

Eksperimenter med forskellige kald af multi_apply_2.


Coding Style

Indrykning
Slide Indhold Stikord
Referencer 

Eksempler på gode og anerkendte indrykningskonventioner, og eksempler på dårlig indrykning

Program: K & R Style.
#include <stdio.h>
 
double f (double x);
int sameSign(double x, double y);
double middleOf(double x, double y);
int isSmallNumber(double x);

/* K & R style */
double findRootBetween(double a, double b)
{
    double l = a, u = b;
    while (!isSmallNumber(f(middleOf(l, u)))) { 
        if(sameSign(f(middleOf(l, u)), f(u)))
            u = middleOf(l, u);
        else 
            l = middleOf(l, u);
    }
    return middleOf(l, u);
}  

int main (void)
{
    findRootBetween(-3.0, 5.0);
}

Program: K & R Style, med curly braces om enkelt sætninger.
#include <stdio.h>
 
double f (double x);
int sameSign(double x, double y);
double middleOf(double x, double y);
int isSmallNumber(double x);

/* K & R style, variant. Curly braces also used around single statements */
double findRootBetween(double a, double b)
{
    double l = a, u = b;
    while (!isSmallNumber(f(middleOf(l, u)))) { 
        if(sameSign(f(middleOf(l, u)), f(u))) {
            u = middleOf(l, u);
        }
        else {
            l = middleOf(l, u);
        }
    }
    return middleOf(l, u);
}  

int main (void)
{
    findRootBetween(-3.0, 5.0);
}

Program: Mindre variant af K & R style. Alternativ opsætning af else.
#include <stdio.h>
 
double f (double x);
int sameSign(double x, double y);
double middleOf(double x, double y);
int isSmallNumber(double x);

/* K & R style, minor variant. More compact setup of else */
double findRootBetween(double a, double b)
{
    double l = a, u = b;
    while (!isSmallNumber(f(middleOf(l, u)))) { 
        if(sameSign(f(middleOf(l, u)), f(u))) {
            u = middleOf(l, u);
        } else {
            l = middleOf(l, u);
        }
    }
    return middleOf(l, u);
}  

int main (void)
{
    findRootBetween(-3.0, 5.0);
}

Program: Meget kompakt formatering - ofte anvendt i disse noter.
#include <stdio.h>
 
double f (double x);
int sameSign(double x, double y);
double middleOf(double x, double y);
int isSmallNumber(double x);

/* A compact formatting - used for many programs in these notes */
double findRootBetween(double a, double b){
  double l = a, u = b;
  while (!isSmallNumber(f(middleOf(l, u)))) { 
    if(sameSign(f(middleOf(l, u)), f(u)))
      u = middleOf(l, u);
    else 
      l = middleOf(l, u);
  }
  return middleOf(l, u);
}  

int main (void){
  findRootBetween(-3.0, 5.0);
}

Program: Allman style.
#include <stdio.h>
 
double f (double x);
int sameSign(double x, double y);
double middleOf(double x, double y);
int isSmallNumber(double x);

/* Allman style */
double findRootBetween(double a, double b)
{
    double l = a, u = b;
    while (!isSmallNumber(f(middleOf(l, u)))) 
    { 
        if(sameSign(f(middleOf(l,u)), f(u))) 
        {
            u = middleOf(l, u);
        } 
        else
        {
            l = middleOf(l, u);
        }
    }
    return middleOf(l, u);
}  

int main (void)
{
    findRootBetween(-3.0, 5.0);
}

Program: Whitesmiths style.
#include <stdio.h>
 
double f (double x);
int sameSign(double x, double y);
double middleOf(double x, double y);
int isSmallNumber(double x);

/* Whitesmiths style */
double findRootBetween(double a, double b)
{
    double l = a, u = b;
    while (!isSmallNumber(f(middleOf(l, u)))) 
        { 
        if(sameSign(f(middleOf(l,u)), f(u))) 
            {
            u = middleOf(l, u);
            } 
        else
            {
            l = middleOf(l, u);
            }
        }
    return middleOf(l, u);
}  

int main (void)
{
    findRootBetween(-3.0, 5.0);
}

Program: Inkonsistent indrykning - blandet anvendelse af indrykningsreglerne.
#include <stdio.h>
 
double f (double x);
int sameSign(double x, double y);
double middleOf(double x, double y);
int isSmallNumber(double x);

/* Inkonsistent indrykning - blandet anvendelse af konventionerne */
double findRootBetween(double a, double b){
    double l = a, u = b;
    while (!isSmallNumber(f(middleOf(l, u)))) 
    { 
        if(sameSign(f(middleOf(l, u)), f(u)))
            u = middleOf(l, u);
        else 
        {
            l = middleOf(l, u);
        }
    }
    return middleOf(l, u);
}  

int main (void)
{
    findRootBetween(-3.0, 5.0);
}

Program: Inkonsistent indrykning - ujævn mængde af indrykning.
#include <stdio.h>
 
double f (double x);
int sameSign(double x, double y);
double middleOf(double x, double y);
int isSmallNumber(double x);

/* En anden variant af inkonsistent indrykning */
double findRootBetween(double a, double b)
{
  double l = a, u = b;
    while (!isSmallNumber(f(middleOf(l, u)))) { 
      if(sameSign(f(middleOf(l, u)), f(u)))
            u = middleOf(l, u);
      else 
        l = middleOf(l, u);
    }
      return middleOf(l, u);
}  

int main (void)
{
findRootBetween(-3.0, 5.0);
}

Program: Udrykning - ikke indrykning.
#include <stdio.h>
 
double f (double x);
int sameSign(double x, double y);
double middleOf(double x, double y);
int isSmallNumber(double x);

/* Udrykning - ikke indrykning */
double findRootBetween(double a, double b)
{
    double l = a, u = b;
    while (!isSmallNumber(f(middleOf(l, u)))) { 
  if(sameSign(f(middleOf(l, u)), f(u)))
      u = middleOf(l, u);
  else 
      l = middleOf(l, u);
  }
        return middleOf(l, u);
}  

int main (void)
{
    findRootBetween(-3.0, 5.0);
}

Program: Så bliver det ikke meget værre...
#include <stdio.h>
 
double f (double x);
int sameSign(double x, double y);
double middleOf(double x, double y);
int isSmallNumber(double x);

/* Just ugly */
double findRootBetween(double a, double b)    {
    double l = a,
  u =   b   ;
    while    (!isSmallNumber(f(middleOf(l, u)))) { 
  if(sameSign(f(middleOf(l, u)), f(u)))
              u = middleOf(l,u);
      else 
            l = middleOf(l,   u);
    }
    return
        middleOf(l,
 u);
}  

int main (void)
{
    findRootBetween(-3.0, 
                    5.0);}

Variabelnavne
Slide Indhold Stikord
Referencer 

Konventioner for brug af sammensatte navne

Program: Lower camel case.
#include <stdio.h>

/* Lower camel case */
double f (double x);
int sameSign(double x, double y);
double middleOf(double x, double y);
int isSmallNumber(double x);

double findRootBetween(double a, double b)
{
    double l = a, u = b;
    while (!isSmallNumber(f(middleOf(l, u)))) { 
        if(sameSign(f(middleOf(l, u)), f(u)))
            u = middleOf(l, u);
        else 
            l = middleOf(l, u);
    }
    return middleOf(l, u);
}  

int main (void)
{
    findRootBetween(-3.0, 5.0);
}

Program: Upper camel case.
#include <stdio.h>

/* Upper camel case */
double f (double x);
int SameSign(double x, double y);
double MiddleOf(double x, double y);
int IsSmallNumber(double x);

double FindRootBetween(double a, double b)
{
    double l = a, u = b;
    while (!IsSmallNumber(f(middleOf(l, u)))) { 
        if(SameSign(f(middleOf(l, u)), f(u)))
            u = middleOf(l, u);
        else 
            l = MiddleOf(l, u);
    }
    return MiddleOf(l, u);
}  

int main (void)
{
    FindRootBetween(-3.0, 5.0);
}

Program: Underscore.
#include <stdio.h>

/* Underscores */
double f (double x);
int same_sign(double x, double y);
double middle_of(double x, double y);
int is_small_number(double x);

double find_root_between(double a, double b)
{
    double l = a, u = b;
    while (!is_small_numerb(f(middle_of(l, u)))) { 
        if(same_sign(f(middle_of(l, u)), f(u)))
            u = middle_of(l, u);
        else 
            l = middle_of(l, u);
    }
    return middle_of(l, u);
}  

int main (void)
{
    find_root_between(-3.0, 5.0);
}

Korte og lange navne

Tilknytning af andre egenskaber til navne

Program: Brug af meget korte navne.
#include <stdio.h>

/* Meget korte navne */
double f (double x);
int ss(double x, double y);
double mo(double x, double y);
int isn(double x);

double frb(double a, double b)
{
    double l = a, u = b;
    while (!isn(f(mo(l, u)))) { 
        if(ss(f(mo(l, u)), f(u)))
            u = mo(l, u);
        else 
            l = mo(l, u);
    }
    return mo(l, u);
}  

int main (void)
{
    frb(-3.0, 5.0);
}

Program: Hungarian notation - forkortede typenavne anvendt som prefix på alle variable.
#include <stdio.h>
#include <math.h>

/* Hungarian notation - abbreviated type names attached to all variables */
void solve_quadratic_equation(double d_a, double d_b, double d_c, 
                              int *pi_number_of_roots, double *pd_root1, 
                              double *pd_root2){
  double d_discriminant;

  d_discriminant = d_b * d_b - 4 * d_a * d_c;

  if (d_discriminant < 0){
    *pi_number_of_roots = 0;
  }
  else if (d_discriminant == 0){
    *pi_number_of_roots = 1;
    *pd_root1 = -d_b/(2*d_a);
  }
  else{
    *pi_number_of_roots = 2;
    *pd_root1 = (-d_b + sqrt(d_discriminant))/(2*d_a);
    *pd_root2 = (-d_b - sqrt(d_discriminant))/(2*d_a);
  }
}   

int main(void) {
  double d_a, d_b, d_c, d_firstRoot, d_secondRoot;
  int i_number_of_roots;
  printf("Enter coeficients a, b, and c: ");
  scanf("%lf %lf %lf", &d_a, &d_b, &d_c);

  if (d_a != 0){
    solve_quadratic_equation(d_a, d_b, d_c, 
             &i_number_of_roots, &d_firstRoot, &d_secondRoot);  
    if (i_number_of_roots == 0)
      printf("No roots\n");
    else if (i_number_of_roots == 1)
      printf("One root: %f\n", d_firstRoot);
    else 
      printf("Two roots: %f and %f\n", d_firstRoot, d_secondRoot);
  }
  else
    printf("The coeficient a must be non-zero");

  return 0;
}

Program: All caps - som et eksempel på at små/store bogstaver kan bruges til at signalere rollen af navne.
#include <stdio.h>

/* Names of constants defined as macroes are ALL CAPS */

#define TABLE_SIZE 11

int main(void) {

  /* Declaration: */
  double table[TABLE_SIZE];
  int i;

  /* Initialization: */
  for(i = 0; i < TABLE_SIZE; i++)
    table[i] = (double)(2 * i);

  /* Printing: */
  for(i = 0; i < TABLE_SIZE; i++)
    printf("%f\n", table[i]);

  printf("\n");
  
  return 0;
}


Ekstra Opgaver

Ekstra Opgaver
Slide Indhold Stikord
Referencer 

Opgave 6.7. En simpel lommeregner

Skriv et program der modellerer en simpel lommeregner. Som altid udvikler vi programmet top-down, med trinvis forfinelse. Lommeregneren holder styr på ét enkelt tal, som indholder 'det hidtidige beregnede resultat': akkumulatoren (som starer med at være 0.0). Hver input-line skal bestå af den næste operation (binær), som skal udføres, efterfulgt af den højre operand til denne operation. (En binær operator tager to operander). Vi antager her at den venstre operand er akkumulatoren (den beregnede værdi, i starten 0.0). Således udfører lommeregneren i hvert trin følgende:

  akkumulator operation højre_opperand

Det anbefales at operationen som lommeregneren skal udføre indlæses som en char med scanf. Den næste højre operand læses naturligvis som en double. Her er en god måde at indlæse disse på:

  scanf(" %c %lf", ...)

Space tegnet foran %c kan være ganske vigtig. Hvorfor? (Læs evt. side 87 i Problem Solving and Program Design in C, 8th edition for at lære mere om dette).

Du skal have en funktion, scan_data, med to output parametre som returnerer en operator og den højre operand fra en data linje, som brugeren taster efter en prompt. (Normalt anbefaler vi at printf/scanf foregår i main. Men som her kan vi naturligvis også have enkelte funktioner, som abstraherer over bruger input/output fra tastatur/skærm).

Du skal også have en funktion, do_next_op, som udfører den påkrævede operation: do_next_op skal have to input parametre (operator og operand) foruden akkumulatoren, som både skal kunne bruges til input og output (og som derfor skal være en pointer).

Her er de gyldige operationer i lommeregneren:

  +    for addition
  -    for subtraktion
  *    for multiplikation
  /    for division
  ^    for potensopløftning
  q    for at komme ud af lommeregneren med slutresultatet

Din lommeregner skal vise den akkumulerede værdi efter hver operation.

Her er et eksempel på en dialog med lommeregneren - som det ser ud når du får skrevet dit program:

  Enter operator and operand: + 5.0
  Result so far is 5.0
  Enter operator and operand: ^ 2
  Result so far is 25.0
  Enter operator and operand: / 2.0
  Result so far is 12.5
  Enter operator and operand: q 0
  Final result is 12.5

(Denne opgave svarer til opgave 10 side 360 i 6. udgave af lærebogen, og den minder om opgave 10 side 391 i 7. udgave).

Opgave 6.7. En valutaomregner

Skriv et C program som konverterer valuta i dollars til euro, kroner, rubler og yen. I kan antage en én dollar er 0.89 euros, 6.66 kroner, 66.43 rubler og 119.9 yen.

I denne opgave skal der indgå en funktion med én input parameter (dollar-beløbet), og fire output parametre (svarende til de fire andre valutaer).

Brug funktionen til at udskrive en omregningstabel til omregning af 1, 2, 3, ... 100 dollars til de fire andre valutaer.

(Denne opgave svarer til programmeringsprojekt 1, side 384 i 7. udgave af lærebogen).


PPM Grafik

PPM grafik funktioner
Slide Indhold Stikord
Referencer 

Vi introducerer et lille og simpelt bibliotek til produktion af PPM grafik

Funktionerne beror på pixel typen, som vi har arbejdet med i en tidligere opgave, og på typen ppm, som repræsenterer et grafisk billede.

PPM billeder refereres gennem pointere

Henvisning

  • De vigtigste PPM funktioner

    • ppm *make_image(unsigned int width, unsigned int height, pixel background_pixel);

    • void set_pixel(ppm *image, unsigned int x, unsigned int y, pixel p);

    • pixel get_pixel(ppm *image, unsigned int x, unsigned int y);

    • void write_image(ppm *image, char *file_name);

Program: Header filen pixel.h.
/* PIXELS - A pixel represents a RGB color. */

/** A new type that represents a single RGB pixel */
typedef unsigned int pixel;

/** The constructor of a pixel in terms of red, green and blue (between 0 and 255) */
pixel make_pixel(unsigned int red, unsigned int green, unsigned int blue);

/** Access and return the red component of the pixel p */
unsigned int get_red(pixel p);

/** Access and return the green component of the pixel p */
unsigned int get_green(pixel p);

/** Access and return the blue component of the pixel p */
unsigned int get_blue(pixel p);

Program: Den tilsvarende fil pixel.c - en mulig implementation.
#include "pixel.h"

pixel make_pixel(unsigned int red, unsigned int green, unsigned int blue){
  return (1 << 24) | (red << 16) | (green << 8) | blue; 
}

unsigned int get_red(pixel p){
  return (p >> 16) & 0xff;
}

unsigned int get_green(pixel p){
  return (p >> 8) & 0xff;
}
unsigned int get_blue(pixel p){
  return p & 0xff;
}

Program: Header filen ppm.h.
#include "pixel.h"

/* PPM IMAGES */

/* A new type that represents a PPM image */
typedef struct ppm{
   unsigned int width;
   unsigned int height;
   unsigned int **pixels;
   } ppm;

/* The constructor of a PPM image. Returns a pointer to a PPM image given the width,
   height and a background pixel (used throughout the entire image).  */
ppm *make_image(unsigned int width, unsigned int height, pixel background_pixel);

/* Set a single pixel in image at (x, y) to p.
   Drawing area: x in [0 .. width-1], y in [0 .. height-1].
   If (x,y) is outside the drawing area, the image is not affected. */
void set_pixel(ppm *image, unsigned int x, unsigned int y,  pixel p);

/* Return the pixel at position (x, y) in image.
   x and y must be within the drawing area:  x in [0 .. width-1], y in [0 .. height-1].*/
pixel get_pixel(ppm *image, unsigned int x, unsigned int y); 

/* Return the width of the image */
unsigned int image_width(ppm *img);

/* Return the height of the image */
unsigned int image_height(ppm *img);

/* Write the PPM image to a file named file_name.*/
void write_image(ppm *image, char *file_name);

/* Read an existing PPM image (P6) from a file named file_name and return it*/
ppm *read_image(char *file_name);

/* Release the resources of the PPM image */
void release_image(ppm *image);

Program: Den tilsvarende fil ppm.c - en mulig implementation.
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "ppm.h"

/* IT IS NOT NECESSARY TO UNDERSTAND THE DETAILS OF THIS PROGRAM.  
   IT IS SUFFICIENT TO UNDERSTAND ppm.h  */

unsigned int **alloc_image(int m, int n){
  int i;
  unsigned int **dpp = (unsigned int **)malloc(m * sizeof(unsigned int *));
  if(dpp == NULL) {
    fprintf(stderr, "out of memory\n");
    exit(1);
  }
  for(i = 0; i < m; i++) {
    if((dpp[i] = (unsigned int *)malloc(n * sizeof(unsigned int))) == NULL) {
        fprintf(stderr, "out of memory\n");
        exit(1);
    }
  }
  return dpp;
}


void init_image(unsigned int **image, unsigned int width, unsigned int height,
                unsigned int red, unsigned int green, unsigned int blue){
  int x,y ;

  for(y = 0; y < height; y++)
    for (x = 0; x < width ; x++){
      image[x][y] = (red << 16) | (green << 8) | blue;
    }
}

ppm *make_image(unsigned int width, unsigned int height, pixel background_pixel){
  /* Allocate the struct: */
  ppm *the_image = malloc(sizeof(ppm));   

  /* Initialize the fields: */
  the_image->width = width;
  the_image->height = height;
  the_image->pixels = alloc_image(width,height);

  /* Initialize all pixels of image: */
  init_image(the_image->pixels, width, height, 
             get_red(background_pixel),
             get_green(background_pixel),
             get_blue(background_pixel));

  /* Return the pointer to the image: */
  return the_image;  
}

void set_pixel(ppm *image, unsigned int x, unsigned int y, pixel p){
  unsigned int **pixel_table = image->pixels;
  if (x >= 0 && x < image->width && y >= 0 && y < image->height)
     pixel_table[x][y] = (unsigned int)p;
}

pixel get_pixel(ppm *image, unsigned int x, unsigned int y){
  unsigned int **pixel_table = image->pixels;
  return (pixel)(pixel_table[x][y]);
}

/* Return the width of the image */
unsigned int image_width(ppm *img){
  return img->width;
}

/* Return the height of the image */
unsigned int image_height(ppm *img){
  return img->height;
}

void write_image(ppm *image, char *file_name){
  FILE *image_file;
  unsigned int **pixel_table = image->pixels;
  unsigned int r, g, b;
  int x, y;
  char width_height_str[50];

  image_file = fopen(file_name, "wb");  

  /* Write PPM header: */
  fputs("P6\n", image_file); 
  sprintf(width_height_str, "%d %d\n", image->width, image->height);
  fputs(width_height_str, image_file);
  fputs("255\n", image_file);

  /* Write pixels: */
  for(y = 0; y < image->height; y++)
    for (x = 0; x < image->width; x++){
         r = (pixel_table[x][y] >> 16) & 0xff;
         g = (pixel_table[x][y] >> 8) & 0xff;
         b = pixel_table[x][y] & 0xff;
         fputc(r, image_file);  fputc(g, image_file); fputc(b, image_file);
    }

  fclose(image_file);
}

int blank_char(int ch){
  return (ch == 32 || ch == 10 || ch == 12);
}

ppm *read_image(char *file_name){
  ppm *the_image = malloc(sizeof(ppm));     /* Allocate the ppm struct: */
  FILE *image_file;
  unsigned int **image;

  int ch, ch1, ch2, red, green, blue,
      width, height, pixel_depth,  x, y;

  image_file = fopen(file_name, "rb");  

  /* Get two first chars - expected 'P6': */
  ch1 = fgetc(image_file); ch2 = fgetc(image_file); 

  if (ch1 == 'P' && ch2 == '6'){
    fscanf(image_file, " %d", &width);  fscanf(image_file, " %d", &height);
    fscanf(image_file, " %d", &pixel_depth);

    if (pixel_depth == 255){
      the_image->width = width;
      the_image->height = height;
      the_image->pixels = alloc_image(width,height);      
      image = the_image->pixels;

      /* Read blank stuff before image: */
      while (blank_char(ch = fgetc(image_file))); 
      ungetc(ch, image_file);

      /* Read the image bytes */
      for(y = 0; y < height; y++)
        for (x = 0; x < width ; x++){
          red = fgetc(image_file); green = fgetc(image_file); blue = fgetc(image_file);
          image[x][y] = (red << 16) | (green << 8) | blue;
        }

      return the_image;
     }
     else {
       printf("Unsupported pixel depth. Bye.");
       exit(-1);
    }
  }
  else {
    printf("The image file does not seem to be PPM, P6 file. Bye.");
    exit(-1);
  }
}

void release_image(ppm *image){
  int i;
  for(i = 0; i <= image->width; i++)
    free(image->pixels[i]);
  free(image->pixels);
  image->pixels = NULL;
  image->width = 0;
  image->height = 0;
}

Henvisning

Program: Et simpelt program der genererer og udskriver et PPM billede.
// Diagonal lines

#include <stdlib.h>
#include <stdio.h>
#include "ppm.h"

int main(void) {

  ppm *img = make_image(500, 500,  make_pixel(255U, 255U, 255U));  // white
  int i;

  pixel black_pixel = make_pixel(0U, 0U, 0U);

  for(i = 0; i < 500; i++){
    set_pixel(img, i, i, black_pixel);
    set_pixel(img, 500 - i - 1, i, black_pixel);
  }

  write_image(img, "img2.pnm");

  return 0;
}

Program: Makefilen som styrer oversættelsen af programmerne.
CC = gcc

prog: ppm.o pixel.o
	$(CC) -lm prog.c pixel.o ppm.o -o prog

ppm.o: ppm.h ppm.c
	$(CC) -c ppm.c

pixel.o: pixel.c pixel.c
	$(CC) -c pixel.c

Henvisning


Samlede referencer
Indhold Stikord
Operator tabellen for C
Tidligere version med input parametre
Rekursive delproblemer
Den iterative funktion findRootBetween
Det oprindelige rodsøgningsprogram
Læs om pointere til funktioner
Slide med opgave om brug af funktionerne fra denne slide
Doxygen dokumentation af ppm.h
Billedet genereret af programmet

 

Kapitel 6: Mere om Funktioner
Kursets hjemmeside     Forfatteren's hjemmeside     Om frembringelsen af disse sider     Forrige lektion (top)     Næste lektion (top)     Forrige lektion (bund)     Næste lektion (bund)     
Genereret: 9. maj 2022, 13:59:10