Denne lektion handler om Pointers
Emnerne behandles i lærebogen på siderne 249 til 305
Pointer er en underlig måde at håndtere data på, man kan ofte opnå samme resultat ved at anvende andre metoder til at udføre den samme funktion på. En af styrkerne i pointere er, at man kan få funktioner og procedure til at returnere mere end en variabel. Ved håndtering af strenge kan man f.eks. få disse til at fylde mindre, når de anvendes i et array. Dette har normalt kun virkelig betydning ved små maskiner eller ved meget store datamængder.
Hvad er en pointer
En pointer giver mulighed for at få fat i variabler og mere
sammensatte datastrukturer uden at referere direkte til variablen. Mekanismen,
som anvendes til dette, er variablens adresse. I praksis betyder det, at adressen
virker som bindeled mellem variablen og programmet, som arbejder med variablen.
Pointere er ikke så uforståelige, som de ofte bliver gjort til. Målet med
denne lektion en langsom at opbygge en forståelse for principperne for pointere
og deres anvendelse.
Hvorfor bruger man så pointere
Pointers bruges meste i tilfælde hvor det at håndtere en
variabel er vanskeligt eller uhensigtsmæssigt. Grunde til at anvende
pointere kan opsummeres således:
Funktionen malloc() vendes der tilbage til i en senere lektion.
Mange programmører påstår, at hvis man anvender pointere oversættes programmerne hurtigere. Den påstand holder ikke med moderne Compilere.
Du har allerede anvendt pointere i forbindelse med funktioner, i scanf() funktionen og ved udskrivning af strenge, anvendte du pointerkonstanter. Pas på, der findes også pointervariabler. Det er netop samarbejdet mellem pointerkonstanter og pointervariabler, som viser pointeres "muskler".
| BEMÆRK, en pointerkonstant er en adresse til en variabel, en pointervariabel er en variabel, som indeholder en adresse. |
Returnering af data fra en funktion
Du har allerede set, hvorledes man kan aflevere flere argumenter til en funktion, men man kan kun
returnere en enkelt værdi fra en funktion. Pointere kan anvendes til at få flere variabler returneret
til den kaldende funktion. Den teknik er måske den mest simple af de ting, man kan anvende pointere til.
Men samtidig er det den mest anvendte teknik i almindelig programkonstruktion.
Eksempel 1 er blot for at repetere, hvad der sker med variabler, som sendes til en funktion ved brug af argumenter i funktionskaldet.
Eksempel 1
void get2tal(int xx, int yy)
{
int temp;
printf("\nAnden udskrift: xx = %d og yy = %d ", xx, yy);
temp = xx;
xx = yy;
yy = temp;
printf("\nTredie udskrift: xx = %d og yy = %d ", xx, yy);
}
void main(void)
{
int x = 4, y = 7;
printf("\nFørste udskrift: x = %d og y = %d ", x, y);
get2tal(x, y);
printf("\nFjerde udskrift: x = %d og y = %d ", x, y);
}
Når man kører programmet, ser man, at tallene, som det kan forventes, er byttet om i tredie udskrift, men der er ikke sket noget med variablerne i fjerde udskrift. Eksemplet, som kan hentes sidst i denne lektion, har fået tilføjet et par linier, som udskriver adresserne til variablerne, her ses det at der er fire forskellige adresser, der er altså oprettet fire forskellige variabler i hukommelsen.
Nu ændres programmet en lille smule. Idet der indføres pointere som variabler i proceduren.
Eksempel 2
void get2tal(int *xx, int *yy)
{
int *temp;
printf("\nAnden udskrift: xx = %d og yy = %d ", *xx, *yy);
*temp = *xx;
*xx = *yy;
*yy = *temp;
printf("\nTredie udskrift: xx = %d og yy = %d ", *xx, *yy);
}
void main(void)
{
int x = 4, y = 7;
printf("\nFørste udskrift: x = %d og y = %d ", x, y);
get2tal(&x, &y);
printf("\nFjerde udskrift: x = %d og y = %d ", x, y);
}
Nu bliver det tilsyneladende kompliceret, men også kun tilsyneladende.
Når du tænker på variabler, er det med deres navne, i dette eksempel altså x og y. Hvis vi vil kende adressen, hvor x og y er gemt, skal vi skrive &x og &y. Det er de adresser, styresystemet tildeler variablerne. Når de erklæres i begyndelsen af programmet. Det er altså konstante pointere, du kan ikke ændre på adresserne, efter at variablerne er erklæret.
Sammenligner vi nu med, hvad get2tal() "tænker", så er det, at det er *xx og *yy, som er variablerne, mens xx og yy er adresserne til variablerne, disse adresser kan vi manipulere, dvs. det er variable pointere.
Det benytter vi os af i eksempel 2, hvor vi bytter værdierne
i proceduren, som nævnt ovenfor var der sat fire variabler af i hukommelsen i
eksempel 1, nemlig x, y, xx og yy.
I eksempel 2 er der kun to variabler, vi
bytter indholdet i adresserne, hvorefter vi rent faktisk har fået byttet
indholdet i de oprindelige x og y.
Konklussion
int *px, *py;
Normalt benytter jeg p foran variabelnavnene, når jeg erklærer pointervariabler, så kan jeg med det samme se hvilken type variabeler, jeg har foran mig i programmet
Klippet ud i pap, int fortæller, at de følgende
pointervariabler kommer til at pege på variabler af typen heltal. * fortæller,
at variablen er en pointer og at den vil indeholde en adresse. px og
py er
navnet på pointervariablerne.
Bemærk at * står foran en enkelt variabel, mens den ved
multiplikation anvendes mellem flere variabler, så compileren vil ikke blive
forvirret i denne situation.
Erklæres: int x, y; er x og y to heltalsvariabler, derefter er &x og &y, henholdsvis x og y's adresse i hukommelsen.
Erklæres: int
*ptrx, *ptry; er *ptrx og *ptry heltalsvariabler, derefter er prtx
og ptry adresserne til *ptrx og
*ptry i hukommelsen.
Dvs. der kan sættes lighedstegn mellem x =
*ptrx; og mellem &x = ptrx;
Erklæres: char navn[81]; og der skrives: printf("%s", navn); giver %s og navn strengens indhold og printf("%d", navn); giver %d og navn adressen til strengens første element.
I det følgende afprøves ovenstående påstande:
Eksempel 3
void main(void)
{
int x = 4, y = 7;
int *px, *py;
printf("\nx er lig med %d, og y er lig med %d\n", x,
y);
px = &x;
py = &y;
*px = *px + 10;
*py = *py + 10;
printf("\nx er lig med %d, og y er lig med %d", x,
y);
}
I eksempel 3 erklæres to variabler x og y, samtidig gives variablerne værdierne henholdsvis 4 og 7. Styresystemet har tildelt dem hver sin adresse i hukommelsen, det er: &x og &y. I næste linie erklæres to pointervariabler *px og *py. En kontroludskrift viser derpå hvad variablerne x og y indeholder. Derpå afleveres x og y's adresser til pointervariablerne. Derpå udføres en beregning med *px og *py. En ny kontroludskrift af x og y viser, at selvom der blev manipuleret med *px og *py, er det indholdet af x og y, som er forandret.
Lad os se hvordan pointere kan påvirke arrays. Først skrives det helt traditionelt uden brug af pointere.
Eksempel 4
void main(void)
{
int numre[] = { 92, 81, 70, 69, 58 };
int tal;
printf("\n");
for (tal = 0; tal<5; tal++)
printf("\nNummer[%d] = %d", tal,
numre[tal] );
}
Dette program kan også skrives om til at anvende pointere
Eksempel 5
void main(void)
{
int numre[] = { 92, 81, 70, 69, 58 };
int tal;
printf("\n");
for (tal = 0; tal<5; tal++)
printf("\nNummer[%d] = %d", tal, *(numre+tal) );
}
Nu er det med at holde ørene stive. numre giver adressen til første element i array'et. Når man derpå lægger 1 til skulle vi forvendte at vi fik næste celle, men her har C's fædre tænkt sig godt om, idet når der arbejdes med pointere, går C op i erklæringen og finder ud af, hvad det er for en type, der er tale om, hvorefter pegepinden flyttes det antal celler, der svarer til, at man kommer til adressen til det næste element i rækken.
Pointere og strenge
Strenge og arrays er meget tæt forbundet. Mange af biblioteksfunktionerne i C
som arbejder med strenge anvender pointere. Lad os se på en som returnere en
pointer.
ptr = strchr(str, 'x');
Pointeren ptr vil få en værdi svarende til
adresse på den første gang man møde x i strengen str. Bemærk det er ikke
positionen i strengen, som jo begynder med 0 (nul), med den faktiske
hukommelsesadresse. F.eks. 8064.
i eksemplet herunder anvendes funktionen.
Eksempel 6
#include <stdio.h>
void main(void)
{
char ch, line[81], *ptr, *strchr();
puts("Indtast en linie der skal gennemsøges: ");
gets(line);
printf("\nIndtast tegne der skal søges efter i teksten: ");
ch = getche();
ptr = strchr(line, ch);
printf("\nTeksten er: %s", line);
printf("\nTeksten starter i adressen %u: \n", line);
printf("Første gang man møder %c er på adressen %u\n", ch,
ptr);
printf("%c står på plads nr %d i teksten\n", ch, ptr-line+1);
}
Du undrer dig måske over udtrykket *strchr();? Funktionen returnere en pointer til et tegn, så det skal erklæres til at være af denne type. Man kan undlade den pågældende erklæring, hvis man i stedet includer biblioteket <string.h>. Det vil man normalt gøre, eksemplet er kun medtaget for at vise, hvorledes man kan løse en sådan opgave uden af medtage biblioteksfunktioner.
Erklæring af et array af pointer, som peger på strenge
Der er en lille forskel, når man vil anvende pointere til array, som indeholder
strenge. Lad os se på et lille eksempel, for at gøre det lettere at forstå
eksemplet, vil jeg anvende et eksempel du kender fra en tidligere lektion. Det
er eksemplet med adgangskontrollen.
I det følgende eksempel er array'et erklæret som strenge der peges på med
pointere.
Eksempel 7
void main(void)
{
int tal;
int godkend = 0;
char navn[40];
char *liste[MAX] =
{ "Katrine",
"Christian",
"Poul",
"Lotte",
"Henning"
};
printf("Indtast dit navn: ");
gets(navn);
for (tal = 0; tal < MAX; tal++)
if ( strcmp (liste[tal],navn) == 0 )
{
godkend = 1;
break;
}
if ( godkend == 1 )
printf("Døren er åben du kan gå ind!");
else
printf("Du har ingen adgang, SKRID!");
}
Hvad betyder udtrykket *liste[MAX]? Normalt skal sådan et udtryk læses på arabisk, dvs. fra højre mod venstre. Se figuren herunder.

I den tidligere version af dette program var strengene gemt i et rektangulært array med 5 rækker og 10 kolonner. I den nye version er strengen gemt i hukommelsen i en lang følge, de udgør på denne måde ikke et array i normal forstand, idet der ikke spildes plads til tomme tegn, dvs. der er ikke nogen spildt plads i hukommelsen.

Det sidste eksempel handler om at få en funktion til at ændre et array og returnere resultatet af ændringen til den kaldende funktion:
void plusti(int *ptr, int
str, int kon)
{
int k;
for(k=0; k<str; k++)
*(ptr+k) = *(ptr+k) + kon;
}
void main(void)
{
int array[SIZE] = { 3, 5, 7, 9, 13 };
int konst = 10;
int j;
for (j=0; j < SIZE; j++)
printf("%d ", *(array+j) );//
Kontroludskrift af array
printf("\n");
plusti(array, SIZE, konst); // kald til funktion
printf("\n");
for (j=0; j < SIZE; j++)
printf("%d ", *(array+j) ); // Vis hvad
array'et indeholder nu
}
Her forsyner det kaldende program funktionen med adressen til et array, dvs.
første argument er en pointer til et array. Næste argument er størrelsen på
array'et. Det sidste argument er den konstant, der skal tilføjes til hver
enkelt af værdierne i array'et.
Øvelse 1
Konstruer et program, hvor man kan indtaste navne til et
array, når det sidste navn er indtastet, skal navnene sorteres i alfabetisk
orden.
Tip: Brug f.eks. if (
strlen(navn[tal] )==0 ) break; til at afslutte
indtastningen af navne til array'et.
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