Zoeken…


Invoering

Een aanwijzer is een adres dat verwijst naar een locatie in het geheugen. Ze worden vaak gebruikt om functies of gegevensstructuren het geheugen te laten kennen en te wijzigen zonder het bedoelde geheugen te hoeven kopiëren. Aanwijzers kunnen worden gebruikt met zowel primitieve (ingebouwde) of door de gebruiker gedefinieerde typen.

Aanwijzers maken gebruik van de operatoren "dereference" * , "address of" & en "arrow" -> . De operatoren '*' en '->' worden gebruikt om toegang te krijgen tot het geheugen waarnaar wordt verwezen, en de operator & wordt gebruikt om een adres in het geheugen op te halen.

Syntaxis

  • <Gegevenstype> * <Naam variabele>;
  • <Gegevenstype> * <Variabele naam> = & <Variabele naam van hetzelfde gegevenstype>;
  • <Gegevenstype> * <Variabelenaam> = <Waarde van hetzelfde gegevenstype>;
  • int * foo; // Een aanwijzer die naar een geheel getal verwijst
  • int * bar = & myIntVar;
  • lange * balk [2];
  • long * bar [] = {& myLongVar1, & myLongVar2}; // Is gelijk aan: lange * balk [2]

Opmerkingen

Let op problemen wanneer u meerdere aanwijzers op dezelfde regel aangeeft.

int* a, b, c; //Only a is a pointer, the others are regular ints.

int* a, *b, *c; //These are three pointers!

int *foo[2]; //Both *foo[0] and *foo[1] are pointers.

Basis van de aanwijzer

C ++ 11

Opmerking: in het volgende wordt ervan uitgegaan dat de C ++ 11 constante nullptr bestaat. Vervang in eerdere versies nullptr door NULL , de constante die vroeger een vergelijkbare rol speelde.

Een pointervariabele maken

Een int *pointer_to_int; kan worden gemaakt met behulp van de specifieke * syntaxis, bijvoorbeeld int *pointer_to_int; .
Wanneer een variabele van het type aanwijzer is ( int * ), bevat deze alleen een geheugenadres. Het geheugenadres is de locatie waar gegevens van het onderliggende type ( int ) worden opgeslagen.

Het verschil is duidelijk bij het vergelijken van de grootte van een variabele met de grootte van een pointer met hetzelfde type:

// Declare a struct type `big_struct` that contains
// three long long ints.
typedef struct {
    long long int foo1;
    long long int foo2;
    long long int foo3;
} big_struct;

// Create a variable `bar` of type `big_struct`
big_struct bar;
// Create a variable `p_bar` of type `pointer to big_struct`.
// Initialize it to `nullptr` (a null pointer).
big_struct *p_bar0 = nullptr;

// Print the size of `bar`
std::cout << "sizeof(bar) = " << sizeof(bar) << std::endl;
// Print the size of `p_bar`.
std::cout << "sizeof(p_bar0) = " << sizeof(p_bar0) << std::endl;

/* Produces:
    sizeof(bar) = 24
    sizeof(p_bar0) = 8
*/

Het adres van een andere variabele nemen

Aanwijzers kunnen net als normale variabelen aan elkaar worden toegewezen; in dit geval is het het geheugenadres dat van de ene aanwijzer naar de andere wordt gekopieerd, niet de feitelijke gegevens waarnaar een aanwijzer verwijst.
Bovendien kunnen ze de waarde nullptr die een nullptr vertegenwoordigt. Een aanwijzer gelijk aan nullptr bevat een ongeldige geheugenlocatie en verwijst daarom niet naar geldige gegevens.

U kunt het geheugenadres van een variabele van een bepaald type krijgen door de variabele vooraf te laten gaan door het adres van operator & . De waarde die wordt geretourneerd door & is een pointer naar het onderliggende type dat het geheugenadres van de variabele bevat (dit zijn geldige gegevens zolang de variabele niet buiten het bereik valt ).

// Copy `p_bar0` into `p_bar_1`.
big_struct *p_bar1 = p_bar0;

// Take the address of `bar` into `p_bar_2`
big_struct *p_bar2 = &bar;

// p_bar1 is now nullptr, p_bar2 is &bar.

p_bar0 = p_bar2;

// p_bar0 is now &bar.

p_bar2 = nullptr;

// p_bar0 == &bar
// p_bar1 == nullptr
// p_bar2 == nullptr

In tegenstelling tot referenties:

  • het toewijzen van twee wijzers overschrijft niet het geheugen waarnaar de toegewezen wijzer verwijst;
  • pointers kunnen null zijn.
  • het adres van de exploitant is expliciet vereist.

Toegang tot de inhoud van een aanwijzer

Omdat het aannemen van een adres & vereist, is voor toegang tot de inhoud ook het gebruik van de dereference-operator * vereist, als voorvoegsel. Wanneer een verwijzing naar een verwijzing wordt verwijderd, wordt deze een variabele van het onderliggende type (eigenlijk een verwijzing ernaar). Het kan dan worden gelezen en gewijzigd, zo niet const .

(*p_bar0).foo1 = 5;

// `p_bar0` points to `bar`. This prints 5.
std::cout << "bar.foo1 = " << bar.foo1 << std::endl;

// Assign the value pointed to by `p_bar0` to `baz`.
big_struct baz;
baz = *p_bar0;

// Now `baz` contains a copy of the data pointed to by `p_bar0`.
// Indeed, it contains a copy of `bar`.

// Prints 5 as well
std::cout << "baz.foo1 = " << baz.foo1 << std::endl;

De combinatie van * en de operator . wordt afgekort door -> :

std::cout << "bar.foo1 = " << (*p_bar0).foo1 << std::endl; // Prints 5
std::cout << "bar.foo1 = " <<  p_bar0->foo1  << std::endl; // Prints 5

Ongeldige verwijzingen verwijderen

Wanneer u een verwijzing naar een aanwijzer ongedaan maakt, moet u ervoor zorgen dat deze naar geldige gegevens verwijst. Verwijdering van een ongeldige aanwijzer (of een null-aanwijzer) kan leiden tot schending van geheugentoegang of tot het lezen of schrijven van afvalgegevens.

big_struct *never_do_this() {
   // This is a local variable. Outside `never_do_this` it doesn't exist.
   big_struct retval;
   retval.foo1 = 11;
   // This returns the address of `retval`.
   return &retval;
   // `retval` is destroyed and any code using the value returned
   // by `never_do_this` has a pointer to a memory location that
   // contains garbage data (or is inaccessible).
}

In een dergelijk scenario geven g++ en clang++ de waarschuwingen correct:

(Clang) warning: address of stack memory associated with local variable 'retval' returned [-Wreturn-stack-address]
(Gcc)   warning: address of local variable ‘retval’ returned [-Wreturn-local-addr]

Daarom is voorzichtigheid geboden wanneer verwijzingen argumenten van functies zijn, omdat deze nul kunnen zijn:

void naive_code(big_struct *ptr_big_struct) {
    // ... some code which doesn't check if `ptr_big_struct` is valid.
    ptr_big_struct->foo1 = 12;
}

// Segmentation fault.
naive_code(nullptr);

Wijzerbewerkingen

Er zijn twee operatoren voor pointers: Operator Adres (&): Retourneert het geheugenadres van de operand. Inhoud-van (Dereference) operator (*): Retourneert de waarde van de variabele op het adres dat is opgegeven door de operator.

int var = 20;
int *ptr;
ptr = &var;

cout << var << endl;
//Outputs 20 (The value of var)

cout << ptr << endl;
//Outputs 0x234f119 (var's memory location)

cout << *ptr << endl;
//Outputs 20(The value of the variable stored in the pointer ptr

De asterisk (*) wordt gebruikt om een aanwijzer te declareren om aan te geven dat het een aanwijzer is. Verwar dit niet met de dereference- operator, die wordt gebruikt om de waarde op het opgegeven adres te verkrijgen. Het zijn gewoon twee verschillende dingen die met hetzelfde teken worden weergegeven.

Aanwijzer rekenen

Verhogen / verlagen

Een aanwijzer kan worden verhoogd of verlaagd (voorvoegsel en postfix). Als u een aanwijzer verhoogt, wordt de aanwijzerwaarde naar het element in de array één element verplaatst voorbij het momenteel naar element verwezen. Door een aanwijzer te verkleinen, wordt deze verplaatst naar het vorige element in de array.

Rekenkundige aanwijzer is niet toegestaan als het type waarnaar de aanwijzer verwijst niet volledig is. void is altijd een onvolledig type.

char* str = new char[10]; // str = 0x010
++str;                    // str = 0x011  in this case sizeof(char) = 1 byte

int* arr = new int[10];   // arr = 0x00100
++arr;                    // arr = 0x00104 if sizeof(int) = 4 bytes

void* ptr = (void*)new char[10];
++ptr;    // void is incomplete.

Als een aanwijzer naar het eindelement wordt verhoogd, wijst de aanwijzer naar een element voorbij het einde van de reeks. Zo'n aanwijzer kan niet worden afgeleid, maar wel worden verlaagd.

Door een aanwijzer naar het one-past-the-end-element in de array te verhogen of door een aanwijzer naar het eerste element in een array te verlagen, krijgt u ongedefinieerd gedrag.

Een aanwijzer naar een niet-arrayobject kan voor de aanwijzer-rekenkunde worden behandeld alsof het een array van grootte 1 is.

Optellen / aftrekken

Geheel getalwaarden kunnen aan verwijzingen worden toegevoegd; ze werken als incrementeel, maar met een specifiek getal in plaats van met 1. Geheel getalwaarden kunnen ook van aanwijzers worden afgetrokken en als aanwijzer afnemend werken. Net als bij het verhogen / verlagen, moet de aanwijzer naar een volledig type wijzen.

char* str = new char[10];  // str = 0x010
str += 2;                  // str = 0x010 + 2 * sizeof(char) = 0x012

int* arr = new int[10];    // arr = 0x100
arr += 2;                  // arr = 0x100 + 2 * sizeof(int) = 0x108, assuming sizeof(int) == 4.

Aanwijzer differentiëren

Het verschil tussen twee verwijzingen naar hetzelfde type kan worden berekend. De twee verwijzingen moeten zich binnen hetzelfde arrayobject bevinden; anders resulteert ongedefinieerd gedrag.

Gegeven twee pointers P en Q in dezelfde array, als P het i -element in de array is en Q het j -element is, dan is P - Q i - j . Het type resultaat is std::ptrdiff_t , van <cstddef> .

char* start = new char[10];  // str = 0x010
char* test = &start[5];
std::ptrdiff_t diff = test - start; //Equal to 5.
std::ptrdiff_t diff = start - test; //Equal to -5; ptrdiff_t is signed.


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow