Sök…


Introduktion

En pekare är en adress som refererar till en plats i minnet. De används ofta för att tillåta funktioner eller datastrukturer att känna till och ändra minne utan att behöva kopiera minnet som det hänvisas till. Pekare är användbara med både primitiva (inbyggda) eller användardefinierade typer.

Pekare använder sig av "dereference" * , "adress till" & , och "pil" -> operatörer. Den '*' och '->' operatörer används för att få åtkomst till minne som pekade på, och & operatören används för att få en adress i minnet.

Syntax

  • <Datatyp> * <Variabelnamn>;
  • <Datatyp> * <Variabelnamn> = & <Variabelnamn på samma Datatyp>;
  • <Datatyp> * <Variabelnamn> = <Värde för samma datatyp>;
  • int * foo; // En pekare som pekar på ett heltal
  • int * bar = & myIntVar;
  • lång * bar [2];
  • long * bar [] = {& myLongVar1, & myLongVar2}; // Lika med: lång * bar [2]

Anmärkningar

Var medveten om problem när du deklarerar flera pekare på samma linje.

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.

Grunderna för pekaren

C ++ 11

Obs: i allt följande antas förekomsten av konstant C ++ 11 nullptr . För tidigare versioner, ersätt nullptr med NULL , konstanten som brukade spela en liknande roll.

Skapa en pekvariabel

En pekvariabel kan skapas med hjälp av den specifika * syntaxen, t.ex. int *pointer_to_int; .
När en variabel är av en pekartyp ( int * ) innehåller den bara en minnesadress. Minnesadressen är den plats där data av den underliggande typen ( int ) lagras.

Skillnaden är tydlig när man jämför en variabel med storleken på en pekare med samma typ:

// 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
*/

Tar adressen till en annan variabel

Pekare kan tilldelas mellan varandra precis som normala variabler; i det här fallet är det minnesadressen som kopieras från en pekare till en annan, inte de faktiska data som en pekare pekar på.
Dessutom kan de ta värdet nullptr som representerar en plats för nollminne. En pekare som är lika med nullptr innehåller en ogiltig minnesplats och hänvisar därför inte till giltiga data.

Du kan få minnesadressen för en variabel av en viss typ genom att prefixera variabeln med adressen till operatören & . Värdet som returneras av & är en pekare till den underliggande typen som innehåller minnesadressen för variabeln (som är giltig data så länge som variabeln inte går ut ur omfattningen ).

// 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

I motsats till referenser:

  • tilldelning av två pekare överskriver inte minnet som den tilldelade pekaren refererar till;
  • pekare kan vara noll.
  • operatörens adress krävs uttryckligen.

Få åtkomst till innehållet i en pekare

Som att ta en adress kräver & , liksom åtkomst till innehållet kräver användning av dereferenceoperatören * , som ett prefix. När en pekare avlägsnas, blir den en variabel av den underliggande typen (faktiskt en referens till den). Det kan sedan läsas och ändras, om inte 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;

Kombinationen av * och operatören . förkortas av -> :

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

Avbryta ogiltiga pekare

När du avleder en pekare bör du se till att den pekar på giltiga data. Att ställa in en ogiltig pekare (eller en nollpekare) kan leda till brist på minnesåtkomst eller att läsa eller skriva skräpdata.

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).
}

I ett sådant scenario ger g++ och clang++ korrekt varningarna:

(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]

Därför måste man vara försiktig när pekare är argument för funktioner, eftersom de kan vara ogiltiga:

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);

Pekaroperationer

Det finns två operatörer för pekare: Operatörens adress (&): Returnerar operandens minnesadress. Content-of (Dereference) -operatören (*): Returnerar värdet på variabeln som ligger på den adress som anges av operatören.

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

Asterisken (*) används för att deklarera en pekare för att enkelt indikera att den är en pekare. Förväxla inte detta med duferensoperatören , som används för att få värdet på den angivna adressen. De är helt enkelt två olika saker representerade med samma tecken.

Pekare Aritmetik

Ökning / minskning

En pekare kan ökas eller minskas (prefix och postfix). Genom att öka en pekare flyttas pekarvärdet till elementet i matrisen ett element förbi det för närvarande pekade elementet. Att minska en pekare flyttar den till föregående element i matrisen.

Pekarens aritmetik är inte tillåten om den typ som pekaren pekar på inte är komplett. void är alltid en ofullständig typ.

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.

Om en pekare till slutelementet ökas, pekar pekaren till ett element förbi matrisens slut. En sådan pekare kan inte avskärmas, men den kan minskas.

Inkrementering av en pekare till elementet som går förbi slutet i matrisen, eller minskning av en pekare till det första elementet i en array ger odefinierat beteende.

En pekare till ett icke-array-objekt kan behandlas för pekarens aritmetik, som om det var en matris med storlek 1.

Tillsats / subtraktion

Heltalsvärden kan läggas till pekare; de fungerar som stegvis, men med ett specifikt nummer snarare än med 1. Heltalsvärden kan också subtraheras från pekare och fungerar som pekardekrementering. Liksom vid inkrementering / minskning måste pekaren peka på en komplett typ.

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.

Pointer Differencing

Skillnaden mellan två pekare till samma typ kan beräknas. De två pekarna måste ligga inom samma arrayobjekt; annars resulterar odefinierat beteende.

Ges två pekare P och Q i samma array, om P är den i : te element i arrayen, och Q är den j : te elementet, då P - Q skall vara i - j . Resultattypen är std::ptrdiff_t , från <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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow