Wat is Pointer?

Een variabele waarvan de waarde het geheugenadres is van een variabele, een array-element, een functie of een ander object. In C wordt een pointer gedeclareerd zoals in int *p; &x levert het adres van x op, en *p leest of schrijft het object waarnaar p wijst. Als een geheel getal bij een pointer wordt opgeteld, schuift die op in eenheden van de grootte van het type waarnaar hij wijst. In de meeste expressies wordt de naam van een array omgezet in een pointer naar het eerste element, zodat a[i] hetzelfde betekent als *(a + i). Een pointerwaarde die nergens naar wijst, heet een NULL-pointer.

Japanse benaming: ポインタ

“Pointer” komt voor in 23 oefenvragen uit 7 onderwerpen, bijv. Basis van programmeren in C, Datastructuren en algoritmen, Instructieset en assembly.

Vakgebied: Basis van programmeren in C

Declaratie en basisbewerkingen

int *p; declareert p als een „pointer naar int”. Nadat p = &x; het adres van de variabele x heeft opgeslagen, schrijft *p = 5; de waarde 5 in x. De argumenten van een functie worden als waarde doorgegeven, dus om een variabele van de aanroeper binnen een functie te veranderen, wordt het adres ervan als pointer doorgegeven. Om via een pointer naar een struct een member te gebruiken, wordt (*p).m geschreven als p->m.

Pointerrekenkunde en arrays

Als 1 wordt opgeteld bij een pointer naar int, schuift het adres niet 1 byte op, maar sizeof(int) bytes (in veel omgevingen 4 bytes). De naam van een array wordt omgezet in een pointer naar het eerste element, behalve in gevallen zoals wanneer hij de operand van sizeof of & is. Als een array als argument aan een functie wordt doorgegeven, wordt dus in werkelijkheid alleen het adres van het eerste element doorgegeven, en sizeof binnen de functie kan de grootte van de hele array niet bepalen.

Gebruik in embedded systemen en aandachtspunten

Op een microcontroller worden registers van randapparaten gelezen en geschreven door een geheel getal als adres om te zetten in een pointer, zoals in (volatile uint32_t *)0x40020014. Lezen of schrijven via een niet-geïnitialiseerde pointer, een NULL-pointer of een dangling pointer naar vrijgegeven geheugen is ongedefinieerd gedrag, en zonder besturingssysteem kan dat ongemerkt andere gegevens beschadigen.

Nu oefenen: vragen over Pointer

Welke beschrijving van een gelinkte lijst (een lijst waarin elk element een pointer naar het volgende element bevat) is juist in vergelijking met een array?

  1. AAls het element vlak vóór de invoegpositie bekend is, kan een element worden ingevoegd door alleen pointers te wijzigen, zonder andere elementen te verplaatsen; om het i-de element te lezen moet de lijst echter vanaf het begin worden doorlopen
  2. BDe positie van het i-de element kan worden berekend, dus elk element kan in constante tijd O(1) worden gelezen
  3. CDe elementen staan zonder tussenruimte aaneengesloten in het geheugen, dus bij het lezen in volgorde werken caches en prefetching goed
Antwoord en toelichting tonen

Antwoord: A. Als het element vlak vóór de invoegpositie bekend is, kan een element worden ingevoegd door alleen pointers te wijzigen, zonder andere elementen te verplaatsen; om het i-de element te lezen moet de lijst echter vanaf het begin worden doorlopen

In een gelinkte lijst kost invoegen of verwijderen O(1) zodra de positie bekend is, want alleen de pointers van de buren hoeven te worden herschreven. Om het i-de element te lezen moeten echter vanaf het begin i pointers worden gevolgd, wat O(n) kost. Een array kan het adres van het i-de element berekenen uit het beginadres en de elementgrootte en leest dus elk element in O(1), maar invoegen in het midden kost O(n), omdat de elementen erachter moeten opschuiven. Bovendien staan de elementen van een array aaneengesloten, zodat caches en prefetching goed werken, terwijl de knopen van een gelinkte lijst vaak verspreid in het geheugen liggen; daardoor kan zelfs een doorloop van dezelfde orde O(n) veel trager zijn.

int is 4 bytes en een pointer is 8 bytes. Als de waarde (het adres) van een pointer p naar int 0x1000 is, wat is dan de waarde van p + 3?

  1. A0x100C
  2. B0x1012
  3. C0x1018
Antwoord en toelichting tonen

Antwoord: A. 0x100C

Als een geheel getal bij een pointer wordt opgeteld, schuift de pointer op in eenheden van de grootte van het type waarnaar hij wijst. int is 4 bytes, dus p + 3 wijst 3 × 4 = 12 bytes verder, naar 0x1000 + 0xC = 0x100C. p + 3 is het adres van het array-element p[3], en *(p + 3) betekent hetzelfde als p[3]. Een pointer naar char schuift telkens 1 byte op, en een pointer naar double (8 bytes) telkens 8 bytes.

Veelgestelde vragen

Hoe groot is een pointer?

In veel 64-bits omgevingen is een pointer 8 bytes, en op 32-bits microcontrollers 4 bytes. Datapointers hebben in het algemeen dezelfde grootte, of ze nu naar char of naar double wijzen. Een pointer is niet noodzakelijk even groot als een int, dus als een adres in een geheel getal moet worden opgeslagen, wordt uintptr_t uit stdint.h gebruikt.

Wat is het verschil tussen const char *p en char *const p?

const char *p is een „pointer naar const char”: de tekens kunnen niet via p worden gewijzigd, maar p zelf kan naar een ander adres worden gezet. char *const p is een „const pointer”: p kan niet worden veranderd, maar waarnaar hij wijst, kan wel worden gewijzigd. Lees het zo: const links van * legt vast waarnaar wordt gewezen, en const rechts van * legt de pointer zelf vast.

“Pointer” in de toelichting

Vragen met “Pointer” (23)

  1. Welke beschrijving van een gelinkte lijst (een lijst waarin elk element een pointer naar het volgende element bevat) is juist in vergelijking met een array?
  2. int is 4 bytes en een pointer is 8 bytes. Als de waarde (het adres) van een pointer p naar int 0x1000 is, wat is dan de waarde van p + 3?
  3. int is 4 bytes en een pointer is 8 bytes. Wat is de uitvoer als het bovenstaande programma wordt uitgevoerd?
  4. Wat is juist over de pointer die de bovenstaande functie make teruggeeft?
  5. Welke beschrijving van wat er in C gebeurt als een NULL-pointer wordt gederefereerd (*p wordt gelezen terwijl p NULL is), is juist?
  6. Waar worden in de standaard RISC-V-aanroepconventie het 9e en 10e argument doorgegeven bij het aanroepen van een functie met 10 gehele-getalargumenten?
  7. Welke omschrijving van de ABI (application binary interface) is juist?
  8. Waarom kan de compiler in de bovenstaande functie de optimalisatie waarbij het lezen van *b buiten de lus wordt gebracht niet zonder meer uitvoeren?
  9. Welk soort instructie wordt gebruikt bij automatische vectorisatie door een compiler?
  10. Wat gebeurt er in het algemeen wanneer een programma een adres benadert dat het besturingssysteem niet heeft toegewezen?
  11. Welke uitleg over het ABA-probleem, dat bij lock-free datastructuren met CAS aandacht vraagt, is juist?
  12. Welke uitspraak over CUDA unified memory is juist?
  13. Een binaire heap wordt opgeslagen in een array a[0], a[1], a[2], … met de wortel in a[0], waarbij elk niveau van bovenaf van links naar rechts wordt gevuld. Wat zijn de kinderen en de ouder van a[4]?
  14. Wat is de meest passende reden dat open adressering met lineair proberen bij een hashtabel met veel elementen en een bezettingsgraad van ongeveer 0,5 vaak sneller is dan chaining (elementen koppelen met gelinkte lijsten)?
  15. Wat legt de C-standaard vast over de grootte van het type int?

Verwante termen

Array Ongedefinieerd gedrag (undefined behavior) Variabele Gelinkte lijst (linked list) Argument Stack (stapelgeheugen) Microcontroller (MCU) Prefetching Dynamische geheugentoewijzing Aanroepconventie (calling convention)

Naar de woordenlijst