C++ sprogets basale typer og kontrolstrukturer

Bjarne Larsen 04. marts 1998

I det følgende er nogle af C++ sprogets basale datatyper og kontrolstrukturer gennemgået ved hjælp af en række eksempler. Eksemplerne er på ingen måde tænkt som en udtømmende beskrivelse af sprogets muligheder; men udelukkende som en introduktion til sproget. Eksemplerne skal suppleres med mere udførlige beskrivelser af bl.a. sprogets syntaks og udviklingsmiljøets muligheder. Der kan i den forbindelse henvises til de relevante manualer (især Users Guide og Programmers Guide), som alle findes on-line i udviklingsmiljøets hjælpe filer.

Eksempel 1. Gentagelser og tælleværker.

Eksempel 2. Sammenligning og forgrening.

Eksempel 3. Gentagelse og opsummering.

Eksempel 4. Opbygning af bibliotek med funktioner.

Eksempel 5. Tabeller.

Eksempel 1. Gentagelser og tælleværker.

Lad maskinen tælle til ti. De enkelte tal udskrives under hinanden på skærmen.

Opstilling af drejebog:

Forventet output fra programmet:

1
2
3
4
5
6
7
8
9
10

Programmets struktur:

Ved valg af en struktur til programmet har man flere muligheder. En nærliggende mulighed ville være at udføre 10 sætninger efter hinanden, der hver udskriver det næste tal i rækken:

int main(int argc, char **argv)
{
cout << " 1\n";
cout << " 2\n";
cout << " 3\n";
cout << " 4\n";
cout << " 5\n";
cout << " 6\n";
cout << " 7\n";
cout << " 8\n";
cout << " 9\n";
cout << " 10\n";
getch();
return 0;
}

Selv om det er forholdsvis let at lave en sådan løsning, er det klart, at den ikke er særlig hensigtsmæssig. Forestil dig f.eks. at du får til opgave at lave et program, som kan tælle til 1.000.000 i stedet for til 10.

Gentagelser og variabler.

En af de vigtigste egenskaber ved en datamaskine er dens evne til udføre den samme instruktion - eller en sekvens af instruktioner - flere gange.

Hvis man skal kunne erstatte de 10 skrivesætninger i det første løsningsforslag med en enkelt sætning, må det, der skal udskrives, kunne varieres, og hermed indføres begrebet en variabel.

I det første løsningsforslag vil det være rimeligt at indføre en numerisk heltalsvariabel, som skal kunne antage værdierne fra 1 til 10. En sådan variabel kan man også kalde en tællevariabel. Herved kan der spares 9 skriveinstruktioner; men til gengæld skal der medtages nogle andre instruktioner.

For det første skal tællevariablen tildeles en startværdi inden gentagelsen begynder. Dette sker - til manges store overraskelse - ikke automatisk.

For det andet skal tællevariablens værdi forhøjes med én, hver gang en værdi er blevet udskrevet i gentagelsen.

Endelig skal det undersøges, om slutværdien er nået, i hvilket tilfælde gentagelsen skal stoppes.

Styring af gentagelser.

Der kan tænkes mange forskellige måder til styring af afslutningen på en gentagelse. Dette indikerer, at gentagelser er meget vigtige konstruktionselementer, som bruges tit, når en databehandlingsopgave skal omsættes til et program. I det følgende skal vi se eksempler på tre forskellige måder at styre gentagelsen på. Som det vil fremgå af eksemplerne, er det ikke lige meget, hvilken metode, man anvender; men det kan være svært for begynderen at afgøre hvilken metode, der er den bedste i en given situation.

// Eksemp1b
int main(int argc, char **argv)
{
int Taeller;
for (Taeller = 1; Taeller <= 10; Taeller++)
{
cout << Taeller << endl;
}
getch();
return 0;
}
// Eksemp1c
int main(int argc, char **argv)
{
int Taeller;
Taeller = 0;
do
{
Taeller++;
cout << Taeller << endl;
}
while (Taeller < 10);
getch();
return 0;
}
// Eksemp1c
int main(int argc, char **argv)
{
int Taeller;
Taeller = 0;
while (Taeller < 10)
{
Taeller++;
cout << Taeller << endl;
}
getch();
return 0;
}

Hvilken styringsmetode til gentagelser er bedst?

Det er ikke altid muligt at udpege en enkelt styringsmetode som værende bedre end andre i en given situation. Ofte er valget af metode et udslag af vane eller smag og behag.

Det vigtigste, man egentlig kan sige om valget af metode, er, at man skal vælge en metode, som man er fortrolig med, fordi man på denne måde undgår at lave fejl i forbindelse med opstilling af betingelser og lignende.

Det skal dog samtidig slås fast, at hvis man behersker flere af de viste styringsmetoder, så har man bedre mulighed for at udarbejde en kort og overskuelig løsning på et givet problem.

I det følgende er der defor forsøgt opstillet nogle tommelfingerregler for, hvornår man bør anvende de forskellige styringsmetoder.

FOR

Denne metode kan med stor fordel anvendes i situationer, hvor en tællevariabel ønskes forøget eller formindsket i faste spring (ofte i spring på én), og hvor start- og slutværdien er et kendt heltal.

DO ... WHILE

Denne metode kan med fordel anvendes, når slutbetingelsen ikke er knyttet til en tællevariabel og gentagelsen skal udføres mindst én gang.

Selv om slutbetingelsen ikke nødvendigvis skal være knyttet til tællevariabel, sådan som i FOR .. TO konstruktionen, kan der dog godt indgå en tællevariabel i slutbetingelsen; men i så fald skal man selv sørge for at initiere den, inden gentagelsen påbegyndes, og man skal også selv forøge eller formindske dens værdi i hvert gennemløb. Det giver til gengæld mulighed for at forøge eller formindske med andre værdier end 1.

WHILE

Denne metode anvendes i situationer, hvor gentagelsesbetingelsen ikke nødvendigvis er knyttet en tællevariabel. I modsætning til DO ... WHILE, som altid vil blive udført mindst én gang, styres WHILE ... DO af en gentagelsesbetingelse, som testes før gentagelsen udføres, og man kan derfor undgå at udføre gentagelsen, hvis gentagelsesbetingelsen ikke er opfyldt fra starten.

Herved kommer gentagelsesbetingelsen til at virke som en vagtpost, der bestemmer, om gentagelsen skal udføres. Heraf følger den engelske betegnelse, som man ofte benytter om denne type gentagelser: sentinel-styrede gentagelser. Det engelske ord sentinel betyder bevogtning.

Der kan godt indgå en tællevariabel i betingelsen; men i så fald skal man selv initiere den, før gentagelsen påbegyndes, ligesom man selv skal forøge eller formindske dens værdi i hvert gennemløb af gentagelsen.

Eksempel 2. Sammenligning og forgrening.

Indlæs tre tal. De to første tal adderes, og summen divideres med det tredie tal, hvis det tredie tal er forskellig fra 0, ellers udskrives en fejlmeddelelse.

Opstilling af drejebog:

1. afprøvning:

Indtast det første tal: 10
Indtast det andet tal: 20
Indtast det tredie tal: 3
Resultatet er: 10

2. afprøvning:

Indtast det første tal: 10
Indtast det andet tal: 20
Indtast det tredie tal: 0
Beregning umulig

Formler:

Beregning: (Tal1 + Tal2) / Tal3, hvis Tal3 forskellig fra 0.

Generelle rutiner:

Udskrivning af en ledetekst efterfulgt af indlæsning af et tal er en handling, som skal udføres 3 gange. Det kan derfor være relevant at generalisere denne handling i en funktion:

int LaesTal(AnsiString Tekst)
{
int Tal;
cout << Tekst;
cin >> Tal;
return Tal;
}
float Beregning(int Tal1, int Tal2, int Tal3)
{
float Resultat;
Resultat = (Tal1 + Tal2) / Tal3;
return Resultat;
}

Programmets struktur:

#include <vcl\condefs.h>
#include <vcl\dstring.h> // inkluderer AnsiString
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream.h>
#include <conio.h>
#pragma hdrstop
//---------------------------------------------------------------------------
USERES("Project1.res");
//---------------------------------------------------------------------------
int LaesTal(AnsiString Tekst)
{
int Tal;
cout << Tekst;
cin >> Tal;
return Tal;
}
float Beregning(int Tal1, int Tal2, int Tal3)
{
float Resultat;
Resultat = (Tal1 + Tal2) / Tal3;
return Resultat;
}
int main(int argc, char **argv)
{
int Tal1, Tal2, Tal3;
Tal1 = LaesTal("Indtast det første tal: ");
Tal2 = LaesTal("Indtast det andet tal: ");
Tal3 = LaesTal("Indtast det tredie tal: ");
if (Tal3 != 0)
{
cout << "Resultatet er: " << Beregning(Tal1, Tal2, Tal3);
}
else
{
cout << "Beregning er umulig";
}
getch();
return 0;
}

Ligesom muligheden for at udføre gentagelser er en fundamental egenskab ved datamaskiner, så er datamaskinens evne til at sammenligne og teste variabler af lige så stor vigtighed. En datamaskine ville nemlig ikke kunne løse vore administrative eller tekniske problemer for os, hvis den ikke kunne sammenligne to variabler og foretage en forgrening af databehandlingen på grundlag af resultatet af denne sammenligning.

Man udtrykker undertiden denne egenskab ved at sige, at datamaskinen er i stand til at udføre logiske valg. Et logisk valg udtrykkes i naturligt sprog ofte med en sætning, der indledes med ordet "hvis": Hvis betingelsen er opfyldt, så udfør denne behandling - ellers udføres en anden behandling.

Betingelser.

En betingelse er en sammensætning af ét eller flere boolske udtryk. Et boolsk udtryk er et udtryk, som kun kan antage én af de to værdier sand og falsk. De enkelte boolske udtryk består normalt af to variabler med en logisk operand imellem. De logiske operander kan være en af nedenstående muligheder:

= lig med

> større end

< mindre end

>= større end eller lig med

<= mindre end eller lig med

!= forskellig fra

Hvis betingelsen er sammensat af flere boolske udtryk, skal de enkelte boolske udtryk være omsluttet af paranteser. Sammenhængen mellem de enkelte boolske udtryk beskrives ved hjælp af logiske operander som AND (&&) og OR (||).

Negation.

Det er muligt ved hjælp af operanden ! at negere et boolsk udtryk. Det vil sige at ændre en værdi fra sand til falsk eller fra falsk til sand. Man skal passe på, hvis det boolske udtryk, som skal negeres, indeholder operander som AND og OR, fordi disse operander ifølge de Morgans lov skifter fra AND til OR eller fra OR til AND i forbindelse med negationen.

Eksempel 3. Gentagelse og opsummering.

Der ønskes udarbejdet et program, som kan bruges ved beregning af gennemsnitshøjden for eleverne i klassen. De enkelte elevers højde indlæses via skærmterminalens tastatur. Når alle højder er indlæst, angives dette overfor programmet ved indtastning af tallet 0.

Opstilling af drejebog:

Indtast elevens højde i cm (stop = 0): 170

Indtast elevens højde i cm (stop = 0): 180

Indtast elevens højde i cm (stop = 0): 190

Indtast elevens højde i cm (stop = 0): 0

Gennemsnitshøjden er beregnet til: 180

Formler:

Gennemsnit: Summen af tal (højder) / Antallet af tal (elever)

Generelle rutiner:

Vi har tidligere lavet en generel rutine til indlæsning af heltal, så den rutine genbruger vi selvfølgelig i dette eksempel:

int LaesTal(AnsiString Tekst)

{

int Tal;

cout << Tekst;

cin >> Tal;

return Tal;

}

float BeregnGennemsnit (int Sum, int Antal);

{

float Gennemsnit;

Gennemsnit = Sum / Antal;

return Gennemsnit;

}

Programmets struktur:

Af drejebogen fremgår det, at indlæsningen af tal skal gentages indtil tallet 0 er indtastet. Dette peger på et sentinel-styret loop (WHILE).

Af hensyn til beregningen af gennemsnit skal der bruges to tælleværker: En sum til opsummering af højder og et antal til opsummering af antal elever. Begge tælleværker nulstilles inden loopet. Efter loopet beregnes og udskrives gennemsnittet.

int main(int argc, char **argv)

{

int Antal;

int Sum;

int Hoejde;

Antal = 0;

Sum = 0;

Hoejde = LaesTal("Indtast elevens højde i cm (stop = 0): ");

while (Hoejde > 0)

{

Antal++;

Sum = Sum + Hoejde;

Hoejde = LaesTal("Indtast elevens højde i cm (stop = 0): ");

}

cout << "Gennemsnitshøjden er beregnet til: " <<

BeregnGennemsnit(Sum, Antal);

getch();

return 0;

}

 

 

Eksempel 4. Opbygning af bibliotek med funktioner.

I eksempel 2 og 3 brugte vi den samme funktion, LaesTal, til indlæsning af heltal. I dette eksempel vil vi generalisere denne funktion, så den bliver generelt anvendelig overalt, hvor man ønsker at indlæse et heltal. I den forbindelse vil vi omdøbe funktionen til LaesInt, fordi dette navn bedre beskriver, hvad funktionen gør. Et tal kan i princippet både være et heltal og et decimaltal. Derfor vil vi også straks tilføje en ny funktion, LaesDouble, som skal kunne indlæse decimaltal af type double eller float.

Biblioteket kaldes MitLib.h og placeres et neutralt sted, hvorfra det kan kaldes, når der er brug for en eller flere af dets funktioner. Vi kan f.eks. oprette det i en ny undermappe med navnet Include i mappen Projects. Biblioteket ser således ud:

#ifndef _MITLIB_

#define _MITLIB_ 1

#include <iostream.h>

#include <vcl/dstring.h>

int LaesInt(AnsiString Tekst)

{

int Tal;

cout << Tekst;

cin >> Tal;

return Tal;

}

int LaesFloat(AnsiString Tekst)

{

float Tal;

cout << Tekst;

cin >> Tal;

return Tal;

}

#endif

Direktiverne #ifndef, #define og #endif skal forhindre, at programkoden i biblioteket bliver inkluderet flere gange i samme program. De to #include direktiver gør det muligt at bruge cout, cin og AnsiString.

Herefter ser programkoden fra eksempel2 således ud:

#include <vcl\condefs.h>

#include <vcl\dstring.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <iostream.h>

#include <conio.h>

#include "../include/mitlib.h"

#pragma hdrstop

//---------------------------------------------------------------------------

USERES("Eksemp4.res");

//---------------------------------------------------------------------------

float Beregning(int Tal1, int Tal2, int Tal3)

{

float Resultat;

Resultat = (Tal1 + Tal2) / Tal3;

return Resultat;

}

 

int main(int argc, char **argv)

{

int Tal1, Tal2, Tal3;

Tal1 = LaesInt("Indtast det første tal: ");

Tal2 = LaesInt("Indtast det andet tal: ");

Tal3 = LaesInt("Indtast det tredie tal: ");

if (Tal3 != 0)

{

cout << "Resultatet er: " << Beregning(Tal1, Tal2, Tal3);

}

else

{

cout << "Beregning er umulig";

}

getch();

return 0;

}

Bemærk den nye #include sætning, som giver adgang til funktionerne LaesInt og LaesFloat.

 

Eksempel 5. Tabeller.

Et biografteater i en større dansk provinsby har fire forestillinger på hver af ugens dage; men billetpriserne varierer i forhold til salget på de forskellige dage og klokkeslæt. Til gengæld er der samme billetpriser overalt i salen.

Udarbejd et program, som på grundlag af en indtastet ugedag (1 - 7) og et indtastet nummer på forestillingen (1 - 4) og det ønskede antal af billetter kan beregne og udskrive prisen. Priserne på de enkelte dage og forestillinger fremgår af nedenstående tabel:

Forestilling 1 2 3 4

Mandag 1 40 40 50 50

Tirsdag 2 40 40 50 50

Onsdag 3 40 40 50 50

Torsdag 4 40 45 50 60

Fredag 5 40 50 55 60

Lørdag 6 50 60 60 70

Søndag 7 50 60 60 70

Opstilling af drejebog:

Indtast dag (mandag = 1 osv., stop = 0): 4

Indtast forestilling (1 - 4): 2

Indtast antal billetter: 2

Pris pr. billet: 45

Pris i alt: 90

Indtast dag (mandag = 1 osv., stop = 0): 8

Ugyldigt dagnr

Indtast dag (mandag = 1 osv., stop = 0): 6

Indtast forestilling (1 - 4): 5

Ugyldig forestilling

Indtast forestilling (1 - 4): 4

Indtast antal billetter: 4

Pris pr. billet: 70

Pris i alt: 280

Indtast dag (mandag = 1 osv., stop = 0): 0

Formler:

Pris i alt = antal * Pris[Dagnr][Forestilling]

Generelle rutiner:

Vi anvender den generelle indlæsningsrutine LaesInt fra MitLib.h.

Programmets struktur:

Af drejebogen fremgår det, at indlæsningen af data skal gentages indtil dagnummer 0 er indtastet. Dette peger på et sentinel-styret loop styret med WHILE.

Når alle data er indlæst slår man op i pristabellen for at finde den aktuelle pris, hvorefter den samlede pris kan beregnes.

#include <vcl\condefs.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <iostream.h>

#include <iomanip.h>

#include <conio.h>

#include "../include/mitlib.h"

 

#pragma hdrstop

//---------------------------------------------------------------------------

USERES("Eksemp5.res");

//---------------------------------------------------------------------------

int main(int argc, char **argv)

{

float PrisTabel[7][4] =

{ {40.0, 40.0, 50.0, 50.0},

{40.0, 40.0, 50.0, 50.0},

{40.0, 40.0, 50.0, 50.0},

{40.0, 45.0, 50.0, 60.0},

{40.0, 50.0, 55.0, 60.0},

{50.0, 60.0, 60.0, 70.0},

{50.0, 60.0, 60.0, 70.0} };

int Dag, Forestilling, Antal;

float Pris;

// Skaf et gyldigt dagnr

do

{

Dag = LaesInt("Indtast dag (mandag = 1 osv., stop = 0): ");

if ((Dag > 7) || (Dag < 0))

cout << "Ugyldigt dagnr" << endl;

}

while ((Dag > 7) || (Dag < 0));

while (Dag != 0)

{

Dag--; // Tabellens index starter ved 0

// Skaf et gyldigt forestillingsnr

do

{

Forestilling = LaesInt("Indtast forestilling (1 - 4): ");

if ((Forestilling > 4) || (Forestilling < 1))

cout << "Ugyldig forestilling" << endl;

}

while ((Forestilling > 4) || (Forestilling < 1));

Forestilling--;

// Indlæs antal

Antal = LaesInt("Indtast antal billetter: ");

Pris = Antal * PrisTabel[Dag][Forestilling];

cout << "Pris pr. billet: ";

cout << setw(8) << PrisTabel[Dag][Forestilling] << endl;

cout << "Pris i alt: ";

cout << setw(8) << Pris << endl;

// Skaf et nyt gyldigt dagnr

do

{

Dag = LaesInt("Indtast dag (mandag = 1 osv., stop = 0): ");

if ((Dag > 7) || (Dag < 0))

cout << "Ugyldigt dagnr" << endl;

}

while ((Dag > 7) || (Dag < 0));

}

getch();

return 0;

}