Kapitel 2
Kontrolstrukturer

Kurt Nørmark ©
Institut for Datalogi, Aalborg Universitet


Sammendrag
Forrige lektion Næste lektion
Stikord Referencer Indhold
I denne lektion gennemgår vi forskellige former for konstrolstrukturer, i hovedsagen forskellige former for selektion og iteration. Som en vigtig og relevant detalje, ser vi også på logiske udtryk, som styrer de fleste kontrolstrukturer.


Kontrolstrukturer - Motivation og Oversigt

Oversigt over kommandoer
Slide Indhold Stikord
Referencer Lærebog 

Vi starter med en oversigt over de kommandoer vi har mødt indtil nu

  • Assignments

    • Udtryk med en assignment operator efterfulgt af semicolon

  • Printf og scanf kommandoerne

    • Disse er reelt funktionskald efterfulgt af semicolon

  • Den tomme kommando

    • Blot et semicolon

Henvisning

Motivation: Primitiv kontrol med hop
Slide Indhold Stikord
Referencer Lærebog 

En goto kommando tillader hop fra ét sted i programmet til et andet.

Programmer med goto kommandoer kan være meget vanskelige af forstå.

Program: Et uddrag af et C program med gotos.
  if (i <= j) goto p1;
  pos = 1; res = i;
  goto p2;
  p1: pos = 2; res = j;

  p2: printf("pos: %d, res: %d\n", pos, res);

Program: Et helt C program med to goto kommandoer.
#include <stdio.h>

int main(void) {

  int i, j, pos, res;
  printf("Enter two integers: ");
  scanf("%d %d", &i, &j);

  if (i <= j) goto p1;
  pos = 1; res = i;
  goto p2;
  p1: pos = 2; res = j;

  p2: printf("pos: %d, res: %d\n", pos, res);
  
  return 0;
}

I næsten alle programmeringssituationer anbefales det at bruge kontrolstrukturer i stedet for goto kommandoer

Goto kommandoer er kun acceptable i undtagelsessituationer, eksempelvis for at komme ud af en 'dyb løkke'.

Oversigt over konstrolstrukturer
Slide Indhold Stikord
Referencer Lærebog 

Begrebet kontrolstruktur: En kontrolstruktur styrer og kontrollerer rækkefølgen af udførelsen af et antal kommandoer

  • Sekventiel kontrol

    • Kommandoer følger efter hinanden i den angivne rækkefølge

    • kommando1; kommando2;

  • Sammensætning

    • En antal kommandoer sammensættes til én enkelt kommando

    • {kommando1; kommando2;}

  • Udvælgelse

    • Én kommando udvælges blandt flere mulige.

    • Udvælgelsen foretages typisk ud fra værdien af et logisk udtryk.

    • if (logiskUdtryk) kommando1; else kommando2;

  • Gentagelse

    • En kommando gentages et antal gange.

    • Antallet af gentagelser styres typisk af et logisk udtryk

    • while (logiskUdtryk) kommando;


Logiske udtryk

Logiske udtryk
Slide Indhold Stikord
Referencer Lærebog 

Begrebet logisk udtryk: Et logisk udtryk beregnes til en værdi i datatypen boolean.
Begrebet boolean: Datatypen boolean indeholder værdierne true og false.

Program: Et uddrag af et C program med logiske udtryk.
  int n = 6;
  
  n == 5
  n >= 5
  n != 5
  n >= 5 && n < 10
  !(n >= 5 && n < 10)
  n < 5 || n >= 10

Henvisning

Program: De boolske udtryk vist i realistiske sammenhænge i et C program.
#include <stdio.h>

int main(void) {

  int n = 6, b;

  printf("%d\n", n == 5);

  if (n >= 5) printf("stor\n"); else printf("lille\n");

  b = n != 5; printf("%d\n", b);

  while (n >= 5 && n < 10) n++;
  printf("%d\n", n);

  if ( !(n >= 5 && n < 10) == (n < 5 || n >= 10))
    printf("OK\n"); 
  else printf("Problems\n");
  
  return 0;
}

I C repræsenteres false af en nulværdi, og true af en vilkårlig ikke-nulværdi

Sandhedstabeller
Slide Indhold Stikord
Referencer Lærebog 

Tabel. Sandhedstabel for not operatoren
A!A
truefalse
falsetrue
 

Tabel. Sandhedstabel for and operatoren
ABA && BA || B
truetruetruetrue
truefalsefalsetrue
falsetruefalsetrue
false falsefalsefalse
 

Tabel. Sandhedstabel for or operatoren
AB!(A && B)!A || !B
truetruefalsefalse
truefalsetruetrue
falsetruetruetrue
false falsetruetrue
 

Prioriteter af logiske operatorer
Slide Indhold Stikord
Referencer Lærebog 

Tabel.
NiveauOperatorerAssociativitet
15()      []      ->      .      ++postfix      --postfix left to right
14++prefix      --prefix      !      ~      sizeof(type)      +unary      -unary      &      * right to left
13*      /      %left to right
12+      -left to right
11<<      >>left to right
10<      <=      >      >=left to right
9==      !=left to right
8&left to right
7^left to right
6|left to right
5&&left to right
4||left to right
3?:right to left
2=      +=      -=      *=      /=      >>=      <<=      &=      ^=      |=right to left
1, left to right
 

Short Circuit evaluering
Slide Indhold Stikord
Referencer Lærebog 

Direkte oversat 'kortsluttet beregning'

Begrebet short circuit evaluering: Short circuit evaluering betegner en beregning hvor visse operander ikke evalueres, idet deres værdi er uden betydning for resultatet

  • x && y

    • y beregnes kun hvis x er true

  • x || y

    • y beregnes kun hvis x er false

Henvisning

Short Circuit evaluering: eksempler
Slide Indhold Stikord
Referencer Lærebog 

Vi viser eksempler på C programmer med short circuit evaluering

Program: Short circuit beregning i kombination med assignments.
#include <stdio.h>

int main(void) {

  int i = 0, j = 0;

  (i = 1) && (j = 2);
  printf("%d %d\n", i, j);

  (i = 0) && (j = 3);
  printf("%d %d\n", i, j);

  i = 0 || (j = 4);     
  printf("%d %d\n", i, j); 

  (i = 2) || (j = 5);
  printf("%d %d\n", i, j);

  return 0;
}

Program: Et ækvivalent program.
#include <stdio.h>

int main(void) {

  int i = 0, j = 0;

  (i = 1) && (j = 2);
  printf("%d %d\n", i, j);

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

  i = 0 || (j = 4);     
  printf("%d %d\n", i, j); 

  (i = 2);
  printf("%d %d\n", i, j);

  return 0;
}

Program: Output fra ovenstående programmer.
1 2
0 2
1 4
2 4

Program: Short circuit beregning som beskyttelse mod fejlberegning.
#include <math.h>
#include <stdio.h>

double sqrt(double);

int main(void) {

  double x;

  printf("Enter a number: ");
  scanf("%lf", &x);

  if (x >= 0 && sqrt(x) <= 10)
    printf("x is positive, but less than or equal to 100.0\n");
  else printf("x is negative or greater than 100.0\n");

  return 0;
}

De øverste programmer er kunstige og konstruerede

Mønstret i det nederste program forekommer ofte i rigtige programmer


Sammensætning af kommandoer

Sammensætning
Slide Indhold Stikord
Referencer Lærebog 

Begrebet blok: En sammensat kommando, også kaldet en blok, er en gruppering af kommandoer til én kommando, hvori der i starten kan forekomme erklæringer af variable

Syntax: En blok

{declaration-or-command-list}

Blokke er ofte nødvendige i forbindelse med udvælgende og gentagende kontrolstrukturer i C.

Blokke kan indlejres i hinanden.

Program: Tre indlejrede blokke med lokale erklæringer af heltal.
#include <stdio.h>

int main(void) {  

  int a = 5, b = 7, c;

  c = a + b;

  { 
    int a = 15, b = 17, c;

    {
      int a = 25, b = 27, c;
      c = a + b;  
      printf("Inner: c er %d\n", c);
    }

    c = a + b;  
    printf("Middle: c er %d\n", c);
  }   

  c = a + b;  
  printf("Outer: c er %d\n", c);
  
  return 0;
}   

Henvisning


Udvælgelse af kommandoer

Udvælgelse med if (1)
Slide Indhold Stikord
Referencer Lærebog 

En selektiv kontrolstruktur udvælger én kommando til udførelse blandt en mængde af muligheder

Syntax: En if-else kontrolstruktur.

if (logicalExpression)
  command1
else
  command2

Figur. Flow graf for if

Else delen kan udelades, hvilket svarer til at command2 er tom

Udvælgelse med if (2)
Slide Indhold Stikord
Referencer Lærebog 

  • Betydning af if-else:

    • Først beregnes det logiske udtryk

    • Hvis værdien er sand udføres command1

    • Hvis værdien er falsk udføres command2

Program: Et program som beregner det største af to doubles.
#include <stdio.h>

int main(void) {

  double x, y, max;

  printf("Enter two real numbers: ");
  scanf("%lf %lf", &x, &y);

  /* find the maximum of x and y */
  if (x < y)
    max = y;
  else
    max = x;

  printf("The maximum of x and y is %12.8f\n", max);
  
  return 0;
}

Program: Goto programmet reformuleret med en if-else kontrolstruktur.
#include <stdio.h>

int main(void) {

  int i, j, pos, res;
  printf("Enter two integers: ");
  scanf("%d %d", &i, &j);

  if (i <= j){ 
    pos = 2; res = j;}
  else {
    pos = 1; res = i;}

  printf("pos: %d, res: %d\n", pos, res);
  
  return 0;
}

Henvisning

Brug af kontrolstrukturer ala if-else er udtryk for struktureret programmering

'Dangling else' problemet
Slide Indhold Stikord
Referencer Lærebog 

Hvis if og if-else kontrolstrukturer indlejres i hinanden kan der opstå tvetydighedsproblemer

Program: Illustration af dangling else problemet.
#include <stdio.h>

int main(void) {

  int a = 3, b = 4;

  if (a == 1)
    if (b == 2)
      printf("A\n");
    else 
      printf("B\n"); 

  if (a == 1) 
    if (b == 2)
      printf("C\n");
  else 
    printf("D\n"); 

  if (a == 1){ 
    if (b == 2)
      printf("E\n");} 
  else 
    printf("F\n"); 
  
  return 0;
}

Program: Output fra ovenstående program.
F

En else knytter sig altid til den inderste if.

Det kan anbefales at bruge sammensætning med {...} hvis man er i tvivl om fortolkningen.

Udvælgelse med switch
Slide Indhold Stikord
Referencer Lærebog 

Syntax: Opbygningen af en switch kontrolstruktur.

switch (expression) {
  case const1: command-list1
  case const2: command-list2
  ...
  default: command-list  
}

  • Betydning:

    • Udtrykket beregnes - skal være et heltal eller heltalsagtigt

    • Hvis værdien svarer til en af konstanterne, flyttes kontrollen til den tilsvarende kommando. De efterfølgende cases udføres.

    • Hvis værdien ikke svarer til en af konstanterne, og hvis der er et default tilfælde, flyttes kontrollen til default kommandoen

    • Hvis værdien ikke svarer til en af konstanterne, og hvis der ikke er et default tilfælde, flyttes kontrollen til afslutningen af switchen

Program: Et program der beregner antal dage i en måned.
#include <stdio.h>

int main(void) {

  int month, numberOfDays, leapYear;

  printf("Enter a month - a number between 1 and 12: ");
  scanf("%d", &month);
  printf("Is it a leap year (1) or not (0): ");
  scanf("%d", &leapYear);

  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 (leapYear) numberOfDays = 29; 
      else numberOfDays = 28; break;
    default: exit(-1);  break;
  } 

  printf("There are %d days in month %d\n", numberOfDays, month);

  return 0;
}

Vi ønsker næsten altid at afslutte et tilfælde i en switch med en break

If-else kæder
Slide Indhold Stikord
Referencer Lærebog 

En if-else kæde er en if-else kontrolstruktur, hvor else-delen igen kan være en if-else struktur

Program: Et program med en if-else kæde der beregner beregner en karakter ud fra et antal procentpoint.
#include <stdio.h>

int main(void) {

  int percent, grade;

  printf("How many percent? ");
  scanf("%d",&percent);

  if (percent >= 90)
    grade = 11;
  else if (percent >= 82)
    grade = 10;
  else if (percent >= 74)
    grade = 9;
  else if (percent >= 66)
    grade = 8;
  else if (percent >= 58)
    grade = 7;
  else if (percent >= 50)
    grade = 6;
  else if (percent >= 40)
    grade = 5;
  else if (percent >= 10)
    grade = 3;
  else grade = 0; 

  printf("%d percent corresponds to the Danish grade %d\n\n", 
          percent, grade);
  
  return 0;
}

Program: Alternativt og uønsket layout af if-else kæde.
#include <stdio.h>

int main(void) {

  int percent, grade;

  printf("How many percent? ");
  scanf("%d",&percent);

  if (percent >= 90)
   grade = 11;
  else if (percent >= 82)
        grade = 10;
       else if (percent >= 74)
             grade = 9;
            else if (percent >= 66)
                  grade = 8;
                 else if (percent >= 58)
                       grade = 7;
                      else if (percent >= 50)
                            grade = 6;
                           else if (percent >= 40)
                                 grade = 5;
                                else if (percent >= 10)
                                      grade = 3;
                                     else grade = 0; 

  printf("%d percent corresponds to the Danish grade %d\n\n", 
          percent, grade);
  
  return 0;
}

En if-else kæde kan generelt ikke programmeres med en switch kontrolstruktur

Den betingede operator
Slide Indhold Stikord
Referencer Lærebog 

I C findes en betinget operator med med tre operander som modsvarer en if-else kontrol struktur.

En forekomst af en betinget operator er et udtryk, hvorimod en forekomst af if-else er en kommando.

Syntax: Opbygningen af den betingede operator i C.

logicalExpression ? expression1 : expression2

  • Betydning af et betinget udtryk:

    • Først beregnes logicalExpresssion

    • Hvis værdien er sand beregnes expression1 ellers expression2, hvorefter den pågældende værdi returneres af ?: operatoren

Program: Et program der bestemmer fortegnet af et tal.
#include <stdio.h>

int main(void) {

  int x;

  printf("Enter an integer: ");
  scanf("%d", &x);

  printf("The sign of %d is %s\n\n", 
         x,
         x < 0 ? "negative" : x == 0 ? "neutral" : "positive");
  
  return 0;
}

Henvisning


Gentagelse af kommandoer

Gentagelse med while (1)
Slide Indhold Stikord
Referencer Lærebog 

I en løkke er det muligt at gentage en kommando nul, én eller flere gange.

I en while løkke er det ubestemt hvormange gange kommandoen udføres.

Syntax: Syntaksen af en while løkke i C.

while (logicalExpression)
  command

Figur. Flow graf for en while løkke

Gentagelse med while (2)
Slide Indhold Stikord
Referencer Lærebog 

  • Betydningen af while:

    • Det logiske udtryk beregnes

    • Hvis værdien er true udføres kommandoen, og while løkken startes forfra

    • Hvis værdien er false afsluttes while løkken

Program: Euclids algoritme - største fælles divisor - programmeret med en while løkke.
#include <stdio.h>

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

  small = i <= j ? i : j;
  large = i <= j ? j : i;
  
  while (small > 0){
    remainder = large % small;
    large = small;
    small = remainder;
  }

  printf("GCD of %d and %d is %d\n\n", i, j, large);
  
  return 0;
}

Program: En udgave af Euclids algoritme som udskriver den beregnede talrække.
#include <stdio.h>

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

  small = i <= j ? i : j;
  large = i <= j ? j : i;

  printf("%d %d ", large, small);
  
  while (small > 0){
    remainder = large % small;
    large = small;
    small = remainder;
    printf("%d ", small);
  }

  printf("\n\nGCD of %d and %d is %d\n\n", i, j, large);
  
  return 0;
}

Gentagelse med do (1)
Slide Indhold Stikord
Referencer Lærebog 

En do-løkke sikrer altid mindst ét gennemløb af løkken

Syntax: Syntaksen af en do-løkke i C

do
  command
while (logicalExpression)

Figur. Flow graf for en do løkke

Gentagelse med do (2)
Slide Indhold Stikord
Referencer Lærebog 

  • Betydningen af do:

    • Kommandoen udføres

    • Det logiske udtryk beregnes

    • Hvis værdien er true overføres kontrollen til starten af do

    • Hvis værdien er false afsluttes do løkken

Program: Et program som ønsker et 'yes/no' svar.
#include <stdio.h>

int main(void) {

  char answer, forget;

  do {
    printf("Answer yes or no (y/n): ");
    scanf("%c%c", &answer, &forget);
  } while (answer != 'y' && answer != 'n');

  printf("The answer is %s\n\n", 
          answer == 'y'? "yes" : "no");
  
  return 0;
}

Gentagelse med for (1)
Slide Indhold Stikord
Referencer Lærebog 

I mange sprog benyttes for kontrolstrukturen hvis man på forhånd kender antallet af gentagelser.

for kontrolstrukturen i C kan benyttes således, men den bruges også ofte til mere generel gentagelse.

Syntax: Opbygningen af en for-løkke i C.

for(initExpression; continueExpression; updateExpression)
  command

  • Betydning af en for-løkke:

    • Trin 1: initExpression evalueres af hensyn til sideeffekter

    • Trin 2: continueExpression evalueres, og hvis den er false (0) er for løkken færdig

    • Trin 3: for løkkens command udføres

    • Trin 4: updateExpression evalueres, og der fortsættes med trin 2

Program: En typisk for-løkke med et forudbestemt antal gentagelser.
#include <stdio.h>

int main(void) {

  int upper, lower, k;

  printf("Enter two integers, lower and upper: ");
  scanf("%d %d", &lower, &upper);

  for (k = lower; k <= upper; k++)
    printf("k = %d\n", k);
  
  return 0;
}

Gentagelse med for (2)
Slide Indhold Stikord
Referencer Lærebog 

Det er muligt at styre en kompleks gentagelse som anvender flere kontrolvariable ved brug af en for løkke

Program: Euclids algoritme - største fælles divisor - programmeret med en for-løkke 'uden krop'.
#include <stdio.h>

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

  for(sml = i<=j?i:j , lg = i<=j?j:i;
      sml > 0;
      rem = lg % sml , lg = sml, sml = rem);

  printf("GCD of %d and %d is %d\n\n", i, j, lg);
  
  return 0;
}

Alle udtryk kan udelades, hvilket afstedkommer en uendelig løkke

Program: En for løkke, der optæller en variabel uendeligt.
#include <stdio.h>

int main(void) {

  long i = 0;

  for( ; ; ++i)
    printf("%ld: Hi there...\n", i);
  
  return 0;
}

Henvisning

Break, continue og return
Slide Indhold Stikord
Referencer Lærebog 

Anvendelse af generelle hop - goto - anses for meget dårlig programmeringsstil.

Brug af 'mere specialiserede hop' bruges oftere.

  • break

    • anvendes til at springe ud af (den inderste) switch, while, do, eller for

  • continue

    • anvendes til at afslutte kommandoen i en while, do eller for løkke

  • return

    • anvendes til at afbryde, og returnere et resultat fra en funktion

Program: Illustration af break og continue i en for-løkke.
#include <stdio.h>

int main(void) {

  long i = 1;

  for( ; ; ++i){
    if (i % 2 == 0) continue;
    if (i > 1000) break;
    printf("%ld: Hi there...\n", i);
  }
  
  return 0;
}

Henvisning

Konvertering af do og for løkker til while
Slide Indhold Stikord
Referencer Lærebog 

Ethvert program med do og for løkker kan let omskrives til kun at bruge while løkker

Tabel.
OriginalTransformation
for (expr1; expr2; expr3)
  command


expr1;
while (expr2) {
  command;
  expr3;
}
do
  command
while (expr)
command;
while (expr)
  command;
 

Læg mærke til, at der for alle gentagende kontrolstrukturer gælder, at kontrol udtrykket er en fortsættelsesbetingelse.

Med andre ord afbrydes alle former slags løkker i C når kontroludtrykkets værdi bliver false.


Samlede referencer
Indhold Stikord
Assignment fra lektion 1
Operator prioriteringer i C
Sandhedstabeller
En senere diskussion af 'scope' ud fra dette eksempel
Goto og motivation for kontrolstrukturer
Operator prioriterings og associeringstabel
Siden med den uendelige for-løkke

 

Kapitel 2: Kontrolstrukturer
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: 7. Juli 2010, 15:10:06