• De afgelopen dagen zijn er meerdere fora waarop bestaande accounts worden overgenomen door spammers. De gebruikersnamen en wachtwoorden zijn via een hack of een lek via andere sites buitgemaakt. Via have i been pwned? kan je controleren of jouw gegeven ook zijn buitgemaakt. Wijzig bij twijfel jouw wachtwoord of schakel de twee-staps-verificatie in.

[C++]Pointers

  • Onderwerp starter pderaaij
  • Startdatum
Status
Niet open voor verdere reacties.
P

pderaaij

Gast
[hand]

Pointers zijn een van de krachtigste hulpmiddelen die je hebt in C++. Het is ook een van de moeilijkste dingen om te leren, maar dat kan ook meevallen. In dit artikel moet je wel weten wat een variabele is, wat voor types je heb en hoe je ze aanmaakt.

Een pointer is een variabele waar je een geheugenadres in opslaat. Waarschijnlijk moet je de regel een paar keer lezen om hem te snappen, maar ik zal hem hier nog even langzaam verklaren. Een pointer is dus een variabele. Dat is dus een object waarin je gegevens kan opslaan. In het geval van een pointer sla je dus in die variabele een geheugenadres op.

Wat is nou een geheugenadres? Het geheugen is verdeeld in hokken, laten we zeggen postvakjes. Ieder postvakje heeft een uniek adres oftewel een uniek geheugenadres. Ieder postvakje is 1 byte. Zo wordt bijvoorbeeld bij een unsigned long int 4 postvakjes gereserveerd. Een unsigned long int is 4 bytes, dus krijgt hij 4 vakjes toegewezen.

Nou hoef je natuurlijk als programmeur de adressen van je variabele weten. Daarvoor heeft C++ een operator voor. Om, achter het adres van een variabele te komen gebruik je de address of operator oftewel &. Om dit te demonstreren dit voorbeeld.

Code:
#include <iostream.h>

int main()
{
unsigned short shortVar=5;

cout << shortVar:t << shortVar;
cout << t Adres van shortVar:t << &shortVar << n;

return 0;
}
OUTPUT:
ShortVar: 5 Adres van shortVar: 0x8fc9:fff4

Verschilt per computer!!!!!

Opslag van een adres

Iedere variabele heeft dus een adres. Nou hoef je dat adres niet te weten, maar dan nog kan je hem opslaan.

Een voorbeeld.

Code:
int myAge = 15; // Maakt variabele myAge
int *pAge = 0; // Maakt pointer pAge

pAge = &myAge ; // zet het adres van myAge in de pointer pAge

We maken dus eerst een variabele. In de 2e regel maken we de werkelijke pointer aan. Dat kun je zien aan het sterretje(*). In de 3e regel wordt in de pointer het geheugenadres van myAge in de pointer gezet.

Wat kun je hier nou mee? Heel simpel je kunt nu met de pointer pAge, de waarde van myAge ophalen. Dit wordt indirectie genoemd. Let wel op, om de waarde op te halen moet je het sterretje(*) voor de pointer zetten, dus het wordt *pAge. Het sterretje(*) wordt ook wel de indirectie-operator genoemd.

n deze tutorial ga ik het hebben over het vrije geheugenruimte, ook wel de heap genoemd. De heap is de ruimte waar variabelen en objecten blijven bestaan tot het moment dat of jij ze verwijdert or totdat het programma is beeindigd.

Hoe werkt dit nu? Kijk even naar het volgende voorbeeld:
Code:
int mijnLeeftijd = 18;
int *pLeeftijd = new int;
pLeeftijd = &mijnLeeftijd;

cout << *pLeeftijd;

Output: 18

Wat heb je nu gedaan? Denk er eerst zelf even over na, dan leg ik het uit....

We beginnen met het maken van een locale variabele in de stack, genaamd mijnLeeftijd en geven die de waarde 18.

In de vrije geheugenruimte maken we een pointer *pLeeftijd groot genoeg om het adres van een int op te slaan.

Het adres van mijnLeeftijd stoppen we in de pointer *pLeeftijd door middel van de & (adress of).

Vervolgens geven we de waarde weer die op het opgeslagen adres staat, 18 dus. Dit proces noemen we indirectie.

Naast variabele werkt dit proces ook met objecten, zoals een class. Hier volgt een voorbeeld.
Code:
#include <iostream.h>

class CSimpleCat
{
public:
CSimpleCat();
~CSimpleCat();
int getAge() const { return *itsAge; }
void setAge(int age) { *itsAge = age; }

int getWeight() const { return *itsWeight; }
int setWeight ( int weight) { *itsWeight = weight; }

private:
int *itsAge;
int *itsWeight;
};

CSimpleCat::CSimpleCat()
{
itsAge = new int(2);
itsWeight = new int(5);
}

CSimpleCat::~CSimpleCat()
{
delete itsAge;
delete itsWeight;
}

int main()
{
CSimpleCat * pFrisky = new CSimpleCat;
cout << "Frisky is "<< pFrisky->getAge() << " years oldn";
pFrisky->setAge(5);
cout << "Frisky is "<< pFrisky->getAge() << " years oldn";
delete pFrisky;
return 0;
}

Zo dat ziet er waarschijnlijk een beetje moeilijk uit, no worry. Hier is de uitleg.

We maken gewoon een class CSimpleCat met een constructor en een aantal accessor functies. Dit moet je allemaal bekend voor komen.

De variabelen zijn ook pointers naar variabelen in de vrije geheugenruimte. Dat hebben we net behandeld en snap je nu denk wel.

In de main functie krijgen we wel wat nieuwe dingen, die ga ik nu behandelen.

Code:
CSimpleCat *pFrisky = new CSimpleCat;

Wat zeggen we hier eigenlijk. In gewoon nederlands zeggen we: "Maak een pointer van het type CSimpleCat in de vrije geheugenruimte met de naam pFrisky wat een CSimpleCat is". Logisch? Niet echt he.

pFrisky is dus van het, zelf gemaakte type, CSimpleCat. Nu zorgen we er dus voor dat pFrisky het juiste aantal bytes heeft die CSimpleCat nodig is. new CSimpleCat zorgt er echter voor dat het ook echt een CSimpleCat wordt.

De eerste CSimpleCat is er dus voor de grootte, de tweede voor de definitie!

Hoe bereik je nu de methoden van CSimpleCat door middel van je pointer? Ook hiervoor moet je indirectie toepassen wat dus zou betekenen dat je het volgende krijgt:

Code:
(*pFrisky).getAge();

Omdat dit nogal omslachtig is heeft men in c++ een verkorte versie gemaakt. De -> operator. Dus nu schrijf je:

Code:
pFrisky->getAge();

Wat nu belangrijk is om niet te vergeten en memory-leaks te voorkomen moet je geheugenruimte vrijmaken. Dit doe je met delete

Code:
delete pFrisky;
pFriksy = 0;

Waarom zetten we pFrisky op 0? Als je dit doet niet krijg je een zogeheten wilde pointer. Het gevaar hiervan is dat als je hem nog een keer verwijdert je in het mooiste geval je programma crasht, maar in het ergste geval je een vastloper krijgt.

Nu kan je namelijk veilig het volgende doen:

Code:
int *pInt = new int;
delete pInt;
*pInt = 0;
delete pInt;


[/hand]
 
Laatst bewerkt:
Nogmaals:

:worship:
 
ik vind het wel een goede tutorial, maar waarvoor worden pointers gebruikt? wat ben je nu met een adres dat opgeslagen wordt als pd22511qsjdfblablabla :huh: :p
 
Beetje late reactie, maar toch nog maar even:
pderaaij zei:
Nou hoef je natuurlijk als programmeur de adressen van je variabele weten.
typfoutje
Code:
int mijnLeeftijd = 18;
int *pLeeftijd = new int;
pLeeftijd = &mijnLeeftijd;

cout << *pLeeftijd;
Deze code zou ik niet zo laten staan, want er zit een memory leak in. Voor iedere keer dat new wordt aangeroepen moet (ergens in het programma) delete een keer worden aangeroepen. Zelfde geldt voor new[] en delete[] (voor dynamische arrays, dus).
In de vrije geheugenruimte maken we een pointer *pLeeftijd groot genoeg om het adres van een int op te slaan.
Je maakt een int aan in de heap en slaat het geheugenadres ervan op in een pointer naar een int in de stack. Omdat je vervolgens een ander geheugenadres toekend aan de pointer, ben je het geheugenadres van de door jou aangemaakte int in de heap kwijt en kun je dit geheugen niet meer vrijgeven. Zoiets heet een memory leak.
Wat zeggen we hier eigenlijk. In gewoon nederlands zeggen we: "Maak een pointer van het type CSimpleCat in de vrije geheugenruimte met de naam pFrisky wat een CSimpleCat is". Logisch? Niet echt he.

pFrisky is dus van het, zelf gemaakte type, CSimpleCat. Nu zorgen we er dus voor dat pFrisky het juiste aantal bytes heeft die CSimpleCat nodig is. new CSimpleCat zorgt er echter voor dat het ook echt een CSimpleCat wordt.

De eerste CSimpleCat is er dus voor de grootte, de tweede voor de definitie!
Je haalt wat dingen door elkaar, denk ik, maar dit is dan ook het stuk waar het lastig wordt.
new maakt een object aan (in dit geval een CSimpleCat) in de heap, dus new bepaald hoeveel geheugenruimte er gereserveerd wordt. Dit wordt erg duidelijk wanneer je een object van een afgeleide klasse aanmaakt met new en de return waarde daarvan (de pointer naar het object dus) toekent aan een pointer naar de basis klasse.
Tegelijkertijd weet de compiler nog altijd de grootte van het object waarnaar een pointer verwijst. Dus de grootte van het object zit hem in het type en niet in de pointer (ook niet in de new, die gebruikt het alleen). Misschien kun je nog wel het beste stellen dat de compiler bepaald hoeveel geheugenruimte door een object wordt ingenomen. Dit weet de compiler, omdat die het type van het object weet.

Nu kan je namelijk veilig het volgende doen:
Code:
int *pInt = new int;
delete pInt;
*pInt = 0;
delete pInt;
Dit gaat dus niet, alhoewel het maar 1 teken scheelt. Je de-referenced (of hoe dat in het nederlands heet) een pointer naar een geheugenadres dat je al hebt vrijgegeven. Je bedoelt natuurlijk "pInt = 0;", maar het is denk ik de moeite waard om op te merken dat ook als pInt = 0; je niet kunt zeggen *pInt. pInt wijst namelijk ook in dit geval niet naar een valide adres waar een int is opgeslagen/gereserveerd (voor stack/heap respectievelijk).
 
Even verplaatst, zodat je er nog aan kunt werken. Als je het af vind, laat me maar weten.
 
Status
Niet open voor verdere reacties.
Steun Ons

Nieuwste berichten

Terug
Bovenaan