5. Lektion
Denne lektion handler Arrays og Strenge
Emnerne behandles i lærebogen på siderne 196 til 237 og 306 til 345
Vi skal i denne lektion beskæftige os med Arrays og Strings. Når jeg
har valgt at tage dette sammen, er det fordi, man kan grundlæggende betragte
strenge, som værende Arrays. I mange lærerbøger om C tager man pointers med
på dette tidspunkt af undervisningen. Jeg syntes, det er svært nok, at få styr
på arrays (lister) og strenge (tekster), så jeg vil vente til næste lektion med at beskæftige
mig med pointerne (pegepinde til hukommelsesadresser).
Jeg vil starte med arrays og slutte med at se
på strenge, idet man kan betragte strengene som et specialtilfælde af arrays .
Et array er en liste med variabler. Variablerne skal være af samme
type for at kunne samles i et array.
Hvis man f.eks. vil beregne gennemsnitstemperaturen for en
uge, kan man oprette en variabel for hver af ugens dag, lægge dem sammen og
dividerer med 7. F.eks.
gensnit = (mantp + tirstp + onstp + torstp + fretp + lortp + sontp) / 7
En sådan beregning vil det være meget lettere at lave
med et
array. Opgaven herover er selvfølgelig overkommelig, men hvis der skulle
beregnes for en måned eller for et år, blev det straks et meget større arbejde at
få opgaven programmeret.
Med et array ser det således ud:
Eksempel 1
#define LIM 40
void main(void)
{
float temper[LIM];
float sum = 0;
int num, dag = 0;
printf("\nIndtast et 0 (nul) for at stoppe programmet\n");
do
{
if ( dag >= LIM )
{
printf("Buffer fuld!\n");
dag++;
break;
}
printf("Indtast en temperatur for %d: ", dag);
scanf("%f", &temper[dag]);
}
while ( temper[dag++] > 0 );
num = dag - 1;
for (dag = 0; dag < num; dag++)
sum += temper[dag];
printf("\nGennemsnittet er %.2f", sum/num);
}
Der er meget nyt i dette program, så lad os se
på det lidt efter lidt. Først skal vi fortælle compileren, at vi vil anvende
et array med decimaltal. Men compileren skal også vide, hvor mange tal der maximalt må være i array'et. Størrelsen bestemmes med #define
LIM 40, som sætter en konstant begrænsning for array'et på 40
elementer. float temper(LIM)
erklærer derpå en liste, som kan indeholde maximalt 40 elementer af typen
float.
Når man derpå skal referere til et element i listen, skriver man f.eks.
temper[3], hvilket giver det 4 element i listen. Hov - der blev sagt fjerde
element i listen, ja, for det første element i listen er temper[0],
eller klippet ud i pap; et array starter altid med
element nummer 0 (nul). Dette betyder igen, at i ovenstående
tilfælde er sidste element i listen temper[39]. Den anden erklæring float
sum = 0; er meget vigtig. Det er en meget almindelig fejl i forbindelse
med programmering, at man glemmer at nulstille sine sum-variabler. I scanf(), skal
man huske at sætte & foran variablen, & betyder, at man aflevere
resultatet af indtastningen til adressen for variablen i stedet for til
variablen direkte. Dette vendes der tilbage til i næste lektion.
Der anvendes en do - while-løkke til at modtage indtastningerne, løkken kører
indtil en temperatur på 0 (nul) eller mindre indtastes. Programmet kan altså ikke arbejde med negative temperaturer.
Man har også en anden måde at erklære
størrelsen af et array. Størrelsen kan sættes automatisk samtidig med tilskrivning af
værdier. Hvis et array tilskrives under erklæringen, som vist herunder vil array'et
automatisk få størrelsen 7.
int tabel[] = {20, 10, 5, 2, 1, 50, 25};
Ovenstående kunne f.eks. være en liste med de mønter, som findes af danske kroner.
Eksempel 2
#define LIM 7 // størrelsen af array'et
void main(void)
{
char a;
float tabel[LIM] = { 20, 10, 5, 2, 1, .50, .25 };
int tal, antal;
float amount;
printf("\nProgram som beregner,
hvor mange mønter et indtaste beløb kan opdeles i\n");
printf("\nIndtast beløbet i kr. (f.eks. 54.25): ");
scanf("%f", &amount);
for (tal=0; tal<LIM; tal++)
{
antal = ((int)(amount*100)) / ((int)(tabel[tal]*100));
printf("Mønter med værdien=%6.2f kr, ", tabel[tal] );
printf("udgør %2d af beløbet!\n", antal );
amount = ((amount / tabel[tal]) - (float)antal) * tabel[tal];
}
a = getch();
}
I nogle tilfælde kan det være praktisk at kunne ændre en type på en
variabel, man har erklæret. Når man ændre en variabels type, siger man, at
man laver et typecast. For at kunne foretage en division med to
decimaltal (float's) og aflevere resultatet i heltalsvariablen antal, ganges
variabelværdierne i den første linie i tælle-løkken med 100, hvorefter der
foretages et typecast, således at float-variablerne bliver til int-variabler.
Ved beregningen af amout skal antal konverteres til float, for at kunne beregne
den nye amount værdi.
I nogle programmer kan der opstå compilerfejl, når man anvender floating point tal. Hvis compileren skriver:
Floating point formats not linked
Skal der tilføjes følgende linier et eller andet sted i den pågældende kildetekst.
extern void
_floatconvert();
#pragam extref _floatconvert
Vi har nu set på endimensionale arrays (vektorer), lad os fortsætte med et todimensionalt array (matrix). Det gør vi med et lille "sænke slagskibe"-program.
Eksempel 3
#define Height 5
#define Width 10
void main(void)
{
char fjende [Height] [Width] =
{ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 1, 0, 0, 1, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
{ 1, 0, 1, 1, 1, 0, 0, 0, 0, 0 }
};
char ven [Height] [Width];
int x, y;
for ( y = 0; y < Height; y++)
for ( x = 0; x < Width; x++)
ven[y][x] = '.';
printf("Indtast koordinater på formen x,y (4,2).\n");
printf("Indtast et negativt tal for at afslutte!\n");
while ( x >= 0 )
{
for (y = 0; y < Height; y++)
{
for ( x = 0; x < Width; x++ )
printf("%c ",
ven[y][x] );
printf("\n\n");
}
printf("Indtast koordinaterne for dit skud: ");
scanf("%d,%d", &x, &y);
if ( fjende[y][x] == 1)
ven[y][x] = 'R'; // Ramt
else
ven[y][x] = 'F'; // Forbier
} // Slut på while
} // Slut på programmet
Ideen med ovenstående program er først og fremmest at
demonstrere et todimensionalt array. Kør programmet og find ud af, hvad der
sker.
Her kommer et program, som finder det største af en række
indtastede tal:
Eksempel 4
#define MaxSize 20
int max(int list[], int size)
{
int t, max;
max =list[0];
for ( t = 1; t < size; t++)
if ( max < list[t] )
max = list[t];
return(max);
}
void main(void)
{
int list[MaxSize];
int tal = 0;
int num;
printf("\nIndtast et tal, eller 0 for at slutte\n");
do
{
printf("Indtast et tal: ");
scanf("%d", &list[tal]);
}
while ( list[tal++] != 0 );
num = max(list,tal-1);
printf("Det største tal er %d", num);
}
Ideen i ovenstående eksempel er, at vise hvorledes man
kan lave en funktion, som tager flere argumenter og hvor det ene argument er et
array.
Funktionen max() starter med at tage det første element i listen og lægge det
ind i variablen max, derefter tæller funktionen sig igennem listen og hvert
element sammenlignes med max. Hvis det aktuelle element er større end max,
skiftes elementets værdi ud med det, der står i max.
Strenge - konstant
Hvis vi skriver printf("%s",
"Kærlig hilsen"); er "Kærlig hilsen" en streng-konstant. At det er en konstant betyder, at den er placeret et eller andet sted i
hukommelsen og at værdien ikke kan ændres. Hvad man ikke kan se umiddelbart er,
at C har tilføjet et tegn til strengen, det er \0. Et tegn fylder normalt 1
byte i maskinens hukommelse. Det ser ud som om der er tilføjet to tegn, men det er
der
ikke, det er en såkaldt escape-sekvens, som er tilføjet. Man siger, at
strengen afsluttes af et terminerende 0
(nul).
Strenge - variable
Før man kan indlæse en streng i et program, skal der sættes
plads af i maskinens hukommelse. Dvs. der skal erklæres en variabel på præcis
samme måde, som vi har set tidligere. Forskellen i forhold til tidligere er, at
der nu ikke er tale om et enkelt tal eller bogstav, men derimod en
sammenhængende række af tegn (en streng).
Eksempel 5
void main(void)
{
char fnavn[15];
printf("\nIndtast dit navn: ");
scanf("%s", fnavn);
printf("Hej med dig, %s", fnavn);
}
I ovenstående program har vi erklæret variablen char fnavn[15]; som betyder,
at vi har oprettet et array på 15 tegn. Betyder det så, at man kan indtaste et
navn på 15 tegn, nej ikke helt, husk hvad der blev sagt ovenfor, nemlig at en
streng altid afsluttes med \0. Dette tegn snupper den sidste plads i strengen,
så der er rent faktisk kun plads til 14 tegn i det indtastede navn. Se figuren
herunder, hvorledes systemet opfatter strengoperationer.

Generelt kan man sige om alle typer arrays, lad være med at indtaste mere, end der er sat plads af til, undgå overflow.
Hvis man er i tvivl om, hvor lang en streng behøver at være, kan man afsætte
81 tegn. Der kan være 80 tegne på tværs af en standardskærm, så en
erklæring som char fnavn[81];
vil i langt de fleste tilfælde være nok.
Når du ser på programmet, opdager du, at scanf("%s",
fnavn); ser anderledes ud, end det du har set tidligere. Ganske rigtigt, der
mangler &, det er derfor, jeg sagde, at man skal være meget opmærksom, når
man anvender scanf(). Jeg fortalte, at & betød at det som
scanf() modtog skal være en adresse. Ved strenge er fnavn netop en
adresse. Variablen er et array og pr. definition er navnet på variablen =
adressen på det første element i strengen. Når navnet allerede er en adresse
behøver det ikke &.
I/O funktionerne gets() og puts()
Jeg har tidligere nævnt at en af svaghederne ved C er
uoverskueligheden af input og output kommandoer. Nu er den gal igen. Man kan
ikke til scanf("%s", fnavn); indtaste Genghis Khan, det kan man godt,
men det eneste som ender i maskinens hukommelse er Genghis. Der skal erindres, at
tal og bogstaver, indtastet til scanf(), kunne adskilles både med Mellemrum, Tab og Return. Løsningen på det problem finder man i en anden
biblioteksfunktion, den hedder gets(), på samme måde som man kunne anvende
printf() til udskrift, findes der en tilsvarende strengfunktion, den hedder
puts(). Begge funktioner findes i biblioteket <stdio.h>. Programmet
herunder demonstrerer, hvorledes funktionerne virker. I de følgende programmer
skal include's <string.h>, når der i programmerne anvendes de specielle
strenghåndteringskommandoer.
Bland aldrig scanf() og gets() i et program.
F.eks. kan følgende give problemer.
scanf("%d", alder);
gets(navn);
Efter at have indtastet en alder, taster man Return, variablen
alder modtager det indtastede tal fint, men Return bliver i tastaturbufferen. Derpå
modtager navn en tom navn fordi, der står et Return klar i bufferen, når gets()
kommer til.
Det bedste, man kan gøre, er at anvende gets() til al input,
hvis man skal arbejde med strenge. En indtastning af 21 vil da blive modtaget
som "21", man må derpå gribe til anvendelsen af konversionsværktøj.
Værktøjet til konvertering af "21" til et tal hedder tal=atoi("21");
på samme måde findes en atof() osv.
Eksempel 6
#include <stdlib.h>
void main(void)
{
char streng[81];
int loeb;
char heat;
float tid;
char navn[81];
printf("\n\nIndtast løbsnummer (1, 2, osv.): ");
gets(streng);
loeb = atoi(streng);
printf("\nIndtast bogstav for heat (A, B, osv.): ");
gets(streng);
heat = string[0];
printf("\nIndtast deltagerens tid (48.35): ");
gets(streng);
tid = atof(streng);
printf("\nIndtast deltagerens navn: ");
gets(navn);
printf("\nDeltager: %s har i heat %c i løb %d, ", navn,
heat, loeb);
printf("\nopnået en tid på %8.2f sekunder.", tid);
}
En anden brugbar funktion er length=strlen(navn);
Et array af strenge
Tidligere i lektionen så vi på et to-dimensionalt array. Man
kan arbejde med noget tilsvarende med strenge.
Det følgende eksempel er et simpelt eksempel på en
adgangskontrol.
Eksemplet beder brugeren om at indtaste et navn, hvorefter
navnet sammenlignes med en navneliste i programmet, hvis det indtastede navn
ikke er i listen afvises personen.
Eksempel 7
#define MAX 5
#define LEN 40
void main(void)
{
int tal; // Tæller i løkken
int enter=0; // Flag som rejses hvis indtastet navn er OK
char navn[40]; // Variabel til navn som indtastes af brugeren
char liste[MAX] [LEN] = // Array med navne
{ "Katrine",
"Peter",
"Alistair",
"Francisca",
"Gustav"
};
printf("\nIndtast dit navn: ");
gets(navn);
for (tal=0; tal<MAX; tal++) // Gå igennem liste
if (strcmp (&liste[tal] [0], navn) == 0 ) // hvis navn er
i liste
{
enter = 1; // Sæt flag til sand
break; // Hop ud af løkken
}
if ( enter == 1 ) // Hvis flaget er rejst
printf("\nVær så god at træde inden for Deres
Højhed!"); // vær flink
else // hvis flaget ikke er rejst
printf("\nVagt! Fjern denne person fra firmaets
område!"); // afvis person
} // Slut på main()
Der er to mulige resultater af at køre dette program enten
får man adgang eller man bortvises.
Bemærk, at der ikke er Tuborg-parenteser omkring navnene i
liste, det var der om de enkelte linier i slagskibs-eksemplet, dette skyldes, at
det handler om strenge, som er array's i sig selv. Læg også mærke til, at de
enkelte elementer i arrayet er adskilt med komma.
Læg endvidere mærke til rækkefølgen af index, hvor array'et bliver erklæret.
Det er vigtigt, at index til antallet af strenge i listen kommer før
strengstørrelsen. Dette er meget vigtigt.
I programmet bliver også introduceret en ny
strenghåndteringsfunktion, det er strcmp(&liste[tal][0], navn) == 0; strcmp()
sammenligner strenge, funktionen returnere et heltal afhængig af udfaldet af
sammenligningen. Det er i tilfældet strcmp(streng1, streng2);
Returneret værdi
Betydning
mindre end nul
streng1 < streng2
nul
streng1 = streng2
større end nul
streng1 > streng2
Betydningen af større end og mindre end er den alfabetiske
rækkefølge, den streng, som er tættest ved A, er den mindste streng.
Betydningen af &liste vendes der tilbage til i næste
lektion.
Denne gang er der et ekstra eksempel, det er et lille program, som kan anvendes
til at undersøge, hvorledes en streng er gemt. Kør programmet! Hvad er det der
sker?
Øvelse 1
Konstruer et program, som kan sortere de tal, som bliver indtastet
til eksempel 4, således at tallene bringes til at stå i stigende orden.
Tip: Brug lærebogens bublesort funktion.
Øvelse 2
Udvid programmet fra øvelse 1 med en lille menu, hvor man kan vælge enten at
få udskrevet max.-værdien eller den sorterede liste.
Eksempler og løsningerne kan hentes her
Eksempel 1
Eksempel 2
Eksempel 3
Eksempel 4
Eksempel 5
Eksempel 6
Eksempel 7
Eksempel 8
Øvelse 1
Øvelse 2