Een variabele
Een variabele in C/C++ gebruikt geheugenruimte. C++ classes gebruiken pas
geheugenruimte als je er objects van maakt. Die objects gebruiken wel
geheugenruimte, zie verderop bij Something.
Macro's zoals
#define SAMPLERATE 48000
gebruiken geen geheugenruimte. Dit is namelijk text-vervanging: overal waar
SAMPLERATE staat in de code wordt het door de preprocessor vervangen door
48000 voordat het de compiler in gaat.
Maar we gaan verder met variabelen en objects.
Als we aannemen dat een
char in het geheugen één byte ruimte nodig heeft en dat het
geheugen in bytes georganiseerd is dan kun je de situatie voorstellen
als hieronder. Adressen zijn in hexadecimale notatie.
Je ziet een plek in het geheugen waar een getal in staat, namelijk het
getal dat volgens de ASCII-tabel correspondeert met de letter 'a'. Zoek
eens op welk getal dat is met
man ascii
Deze geheugenplaats heeft een label
letter. Dat is de naam van de
variabele. Het label weet ook dat het hier gaat om een char, wat in C++
onder meer betekent dat het in één byte past.
In C++ schrijf je dit op als
char letter = 'a';
en dan denk je misschien "ja dat weet ik ook wel", maar realiseer je je dat
dit twee stappen zijn?
- maak geheugenruimte vrij voor het bewaren van een char
- schrijf het getal dat overeenkomt met 'a' in die geheugenruimte
In werkelijkheid zal geheugen meestal niet per byte toegankelijk zijn maar
bijvoorbeeld per 8 bytes (64 bits) en een char zal misschien een hele
64-bits-plek innemen of samengevoegd worden met andere chars. Bovendien
gebruiken pointers in de volgende voorbeelden 4 bytes, wat in werkelijkheid
vaak 8 bytes zullen zijn, maar het gaat om het idee.
Een pointer naar een variabele
Hieronder zie je een pointer letterPointer die geheugenruimte
gebruikt. Een pointer is namelijk in essentie ook een variabele.
Als je een pointer maakt maar geen waarde geeft dan kan er een
willekeurige waarde in staan.
Wanneer we de pointer een waarde geven dan kunnen we deze gebruiken om naar
een plaats in het geheugen te wijzen. In het volgende plaatje wijst
letterPointer naar de geheugenplaats van de variabele letter doordat we het
adres van de variabele letter (hier hexadecimaal 0x0d of in tientallig
stelsel 13) aan letterPointer toegekend hebben. Tel maar na als je het
precies wilt weten: 'a' staat op plaats 13.
char letter = 'a';
char *letterPointer = &letter;
We kunnen nu de inhoud van letter op twee manieren benaderen: via de
variabele letter en via letterPointer:
std::cout << letter << std::endl;
std::cout << *letterPointer << std::endl;
Het sterretje voor letterPointer geeft aan dat we niet de inhoud van
letterPointer willen zien maar de inhoud van de plaats waar letterPointer
naar wijst.
In de volgende plaatjes zie je telkens hetzelfde principe: een pointer die
het adres krijgt van de eerste byte van een variabele en door middel van
het type van de pointer weet uit hoeveel bytes de variabele bestaat. Zo heb
je bijvoorbeeld een char-pointer, int-pointer, pointer naar double of
pointer naar een object.
Merk op dat in alle gevallen de pointer zelf 4 bytes nodig heeft,
onafhankelijk van waar die naar wijst.
int year = 0x7e2;
int *yearPointer = &year;
long year = 0x7e2;
long *yearPointer = &year;
Something something;
Something *somethingPointer = &something;
void pointers
void* aPointer;
Een void pointer is een pointer waarvan je alleen weet naar welke plek in
het geheugen hij wijst, maar aan de pointer zelf kun je niet zien wat voor
data op die plek staan. Je weet dus niet of er een int, long, double of
eigen type verwacht wordt maar alleen waar naartoe de pointer wijst. Je
bent als programmeur verantwoordelijk om aan te geven hoe je de data waar
de pointer naar wijst wilt interpreteren.
Wandelen met pointers
Een pointer hoeft natuurlijk niet zo lang je programma draait naar dezelfde
plek te wijzen. Je kunt er een ander adres in schrijven en dan iets lezen
of schrijven op de plek waar deze dan naartoe wijst. Zo kun je bijvoorbeeld
door een array heen wandelen:
std::string names = {"Kees","Marie","Tom","Els"};
std::string *namePointer=names;
for(int name=0; name<4; name++){
std::cout << *namePointer << std::endl;
namePointer++;
}
Let bij dit voorbeeld wel op dat je niet buiten de array leest of schrijft.
Als je 4 verandert in 5 of 10 of 20000 gaat de compiler dit gewoon toestaan.
Het programma zal misschien niet crashen maar het gedrag is onvoorspelbaar.
Voer het een paar keer uit en kijk wat er gebeurt. Als het niet uit
zichzelf stopt dan kun je ^C (control-C) gebruiken.
Voorbeelden van pointers
Hier staan
diverse voorbeelden