Forudsætninger: for at forstå pointere er det nødvendigt at forstå variabler og arrays. Det anbefales at lære om funktioner først, men det er ikke nødvendigt.
Pointere er et vigtigt element i C og endnu mere i C++, denne tutorial vil kun gennemgå den mest simple pointer teori, og der vil senere blive skrevet om mere advancerede anvendelser af pointere.
en pointer er en type variabel, den minder en hel del om int men i stedet for et heltal indeholder den en adresse til en anden variabeltype, dette lyder måske i første omgang som en fjollet ide, men det viser sig at have fornuftige anvendelser.
For at lave en pointer i C og C++ sættes en stjerne foran variabelnavnet, f.eks. int *i; dette laver pointeren i af typen int, skal man bruge værdien der står på i's plads skriver man *i, og skal man ændre adressen skrives der blot i. ofte vil man gerne have en variabels adresse som man kan sætte i til og så sætter man bare et & foran dennes navn. f.eks. int *i=&j; nu indeholder i så adressen på integer værdien j og *i og j er herefter synonyme.
Den første og mest åbenlyse anvendelse er med funktioner, når man kalder en funktion kopieres argumenterne over til den nye funktion, men nogle gange vil man godt have lov til at ændre en variabel i en anden funktion en der hvor den er oprettet. Hvis man så i stedet bruger en pointer laves der en kopi af pointeren som jo er en reference til en adresse og funktionen kan så skrive der hvor adressen peger hen. Eksemplet på hvordan en pointer sendes til en funktion er dog lagt under funktioner.
En anden anvendelse af pointere er arrays, når man laver et array laver computeren plads til arrayets størelse i hukommelsen og der laves en pointer til element 0, man kan derfor også bruge en pointer til at bladre gennem et array, dette er fornuftigt i tilfælde hvor man er i tvivl om hvorvidt ens compiler optimerer ordentligt og speed er et absolut 'must have'(burde ikke betyde noget på en almindelig moderne computer). Jeg tror dette kræver et eksempel og en efterfølgende forklaring (hvis ikke du behøver forklaringen er det ret sejt :-P).
001 #include <windows.h>
002
003 void strcpyP(char *ud,const char *ind);
004 void strcpyA(char *ud,const char *ind);
005
006 int WINAPI WinMain(HINSTANCE hThisInstance,HINSTANCE hPrevInstance,LPSTR lpszArgument,int nFunsterStil){
007 char alf[]="jaaaaa det virker";
008 char test1[512];
009 char test2[512];
010 strcpyP(test1,alf);
011 MessageBox(0,alf,test1,MB_OK);
012 strcpyA(test2,alf);
013 MessageBox(0,alf,test2,MB_OK);
014 return 0;
015 }
016
017 void strcpyP(char *ud,const char *ind){
018 do{
019 *ud++=*ind;
020 }while(*ind++);
021 }
022
023 void strcpyA(char *ud,const char *ind){
024 int i;
025 for(i=0;ind[i];i++)
026 ud[i]=ind[i];
027 ud[i]=0;
028 }
ok så teknisk set gør de 2 funktioner det samme, men måden de gør det på er ikke helt ens, pointer udgaven løber igennem arrayet med 2 pointere i stedet for 1 int, årsagen til at dette er mere effektivt er at der for hver gang der laves en [] indekserings operator regnes forfra dvs. den lægger tallet i klammerne til pointeres værdi og ser hvad der står på denne plads, dette er selvfølgelig ikke store beregninger og de kører kun med heltal, udover dette optimeres der lidt i koden når den compileres. Så i sidste ende er det ligegyldigt, begge måder er korrekte, og jeg vil mest benytte array metoden, i hvert fald indtil det handler om kædede lister.
Jeg går udfra på dette tidspunkt at læseren er indforstået med at alle strenge slutter med 0 og at 0 er det eneste tal der regnes for falskt i en betingelse.
Der er også en tredje anvendelse af pointere der hører til i denne del, uheldigvis er det også der hvor folk ofte begynder at lave fejl. En pointer kan nemlig også sættes til at pege på nye elementer (altså elementer der ikke er variabler i forvejen). Måden dette gøres på er lidt forskellig fra C til C++ årsagen er at C++ indfører objekter i C og dette gjorde det nødvendigt at have en fornuftig måde at allokere nye objekter på. Objekter vil ikke blive beskrevet her da de foreløbig ikke er nødvendige og da de fortjener adskillige tutorials for at komme ordentligt omkring dem, og ud i alle hjørner.
Når man beslutter sig for at lave et nyt element i C og C++ er det også nødvendigt at slette det igen, årsagen er at C og C++ som standard ikke ved hvornår et element ikke skal bruges mere, dette 'problem' kan 'nemt' løses men forståelsen af måden dette foregår på ligger endnu et stykke uden for læserens nuværende formodede forståelse af C++.
Jeg vil først forklare hvordan det gøres i C og derefter hvordan det gøres i C++.
i C kan man reservere en plads i hukommelsen med funktionen malloc, malloc tager 1 argument som er antallet af byte der skal reserveres. Når man er færdig med at bruge elementet skal man så huske at kalde free, free tager også 1 argument nemlig pladsen hvor der skal slettes en reservation fra. Jeg tror et eksempel er en god ide.
001 #include <windows.h>
002
003 int WINAPI WinMain(HINSTANCE hThisInstance,HINSTANCE hPrevInstance,LPSTR lpszArgument,int nFunsterStil){
004 char fil_buffer[] = "Dette her skal forstille at være indlæst fra en fil!";
005 char *tekst;
006
007 tekst = (char *)malloc(sizeof(fil_buffer)); //det kunne så erstattes med størrelsen af filen
008 strcpy(tekst,fil_buffer); //dette ville så være en indlæsning fra filen
009 MessageBox(0,tekst,"tekst:",MB_OK);
010 free(tekst);
011 }
Jeg bør måske lige nævne af sizeof finder størrelsen af et element, f.eks. sizeof(int) eller sizeof(double), på den måde kan man sikre sig at der er plads til variablen. grunden til at der står (char *) mellem = og malloc er at malloc returnerer en pointer til en void (void *) og man er derfor nødt til at tvinge den til den rigtige type. Denne måde at tvinge en type på kaldes et cast.
Fordelen her er åbenlys, i hvert fald hvis det havde været en fil, for så havde det ikke været nødvendigt at vide på forhånd hvor stort et array der skal laves. Ulempen er at hvis filen er for stor fejler malloc og vi har ikke noget fejlcheck her, hvis malloc fejler returnerer den 0 som adresse, nogle foretrækker null eller NULL men disse er i teorien det samme som 0. I andre situationer kan dynamisk hukommelses allokering også have fordele, men de må blive gemt til det bliver relevant.
I C++ laves dynamisk hukommelses allokering med new operatoren, når den så skal slettes igen bruges delete, fordelen ved at bruge new frem for malloc vil først blive tydelig senere, så for nu vil jeg blot give et eksempel på anvendelse.
001 #include <windows.h>
002
003 int WINAPI WinMain(HINSTANCE hThisInstance,HINSTANCE hPrevInstance,LPSTR lpszArgument,int nFunsterStil){
004 char fil_buffer[] = "Dette her skal forstille at være indlæst fra en fil!";
005 char *tekst;
006 tekst = new char[sizeof(fil_buffer)];
007 strcpy(tekst,fil_buffer);
008 MessageBox(0,tekst,"tekst:",MB_OK);
009 delete[] tekst;
010 }
Dette er igen et array, men metoden er den samme for et enkelt element, [] skal bare fjeren sammen med det der står indeni. Hvis new fejler kaster den en exception, dette vil først blive forklaret senere, så for nu bør du skrive new(nothrow), på denne måde fejler den på samme måde som malloc og man kunne så have en if(tekst){ lige efter allokeringen og så slutte med }else MessageBox(0,"Der er ikke plads i hukommelsen","Fejl",MB_ICONERROR); det færdige resultat bliver:
001 #include <windows.h>
002 #include <new>
003
004 using namespace std;
005
006 int WINAPI WinMain(HINSTANCE hThisInstance,HINSTANCE hPrevInstance,LPSTR lpszArgument,int nFunsterStil){
007 char fil_buffer[] = "Dette her skal forstille at være indlæst fra en fil!";
008 char *tekst;
009 tekst = new(nothrow) char[sizeof(fil_buffer)];
010 if(tekst){
011 strcpy(tekst,fil_buffer);
012 MessageBox(0,tekst,"tekst:",MB_OK);
013 delete[] tekst;
014 }else
015 MessageBox(0,"Der er ikke plads i hukommelsen","Fejl",MB_ICONERROR);
016 }
linie 2 og 4 er der for at få new(nothrow) med, linie 4 siger at den skal bruge std namespace, dette blev indført i C++ for at undgå at overskrive de gamle C header filer med nye, man kaldte så C++ headerne det samme men undlader .h og så skal man huske at bruge std som namespace. Namespace vil blive forklaret i nærmere detaljer senere.
Jeg lader det være op til læseren at skrive en fejlhåndtering til C udgaven, da den ligner C++ udgaven fuldstændig.
Som altid: Hvis der er nogen spørgsmål så send mig en mail på:
admin@AODASoft.net