Beginner's Guide

# PointersPointersPointers

## Why do we need them?Why?

• indirection without copying: referencing / keeping track of objects
• want to change target objects at runtime ⇒ can't use references
• access objects of dynamic storage duration i.e., objects whose lifetime is not tied to a variable / a scope (in later chapters) ## Pointer to Object of Type `T`Pointer to `T`

• essentially an (unsigned) integer variable storing a memory address
• size: 64 bits on 64 bit platforms
• many raw pointers can point to the same address / object
• lifetimes of pointer and target object are independent

### Smart Pointers C++11

We will learn how to use these smart pointers in later chapters.

## SyntaxSyntax

`*` `&`
###### Type Modifier

Pointer Declaration

``Type* ptr = nullptr;``

Reference Declaration

``Type& ref = variable;``
###### Unary Operator

Dereferencing

``value = *pointer;``

``pointer = &variable;``
###### Binary Operator

Multiplication

``product = expr1 * expr2;``

Bitwise AND

``bitand = expr1 & expr2;``
``````int*  p1, p2;    // int*, int
int  *p1, *p2;   // int*, int*``````
``````int* p1 = …;
int* p2 = …;``````

## Redirection

``````#include <iostream>

int main () {int a = 0;
int b = 0;
int* p = &a;
*p = 2;
p = &b;
*p = 9;
std::cout << a << '\n';  // 2
std::cout << b << '\n';  // 9
}``````
``````
a: 0    b: 0
p→a   a: 0    b: 0
p→a   a: 2    b: 0
p→b   a: 2    b: 0
p→b   a: 2    b: 9

``````

## `nullptr``nullptr`C++11

• special pointer value
• is implicitly convertible to `false`
• not necessarily represented by `0` in memory! (depends on platform)
• set pointer to `nullptr` or valid address on initialization
• check if not `nullptr` before dereferencing
``````int* p = nullptr;   // init to nullptr
if (…) {
int i = 5;
p = &i;  // assign valid address
…
// check before dereferencing!
if (p) *p = 7;
…
// set to nullptr, signalling 'not available'
p = nullptr;
}
// i's memory is freed, // any pointer to i would be invalidated!``````

## `const` and Pointers`const`

2. preventing pointer redirection

### Syntax

pointer to type `T` pointed-to value modifiable pointer itself modifiable
`T *`
`T const *`
`T * const`
`T const * const`

### Examples

``````int i = 5;
int j = 8;
int const* cp = &i;
*cp = 8;   //  COMPILER ERROR: pointed-to value is const
cp = &j;   //  OK
int *const pc = &i;
*pc = 8;   //  OK
pc = &j;   //  COMPILER ERROR: pointer itself is const
int const*const cpc = &i;
*cpc = 8;  //  COMPILER ERROR: pointed-to value is const
cpc = &j;  //  COMPILER ERROR: pointer itself is const``````

### Style: East `const` vs. `const` West An ongoing debate about style…Style

one consistent rule:
What's left of `const` is constant

(still) more widespread, but less consistent

``````int const c = …;
int const& cr = …;
int const* pc = …;
int *const cp = …;
int const*const cpc = …;``````
``````const int c = 1;
const int& cr = …;
const int* pc = …;
int *const cp = …;
const int *const cpc = …;``````

## The `this` Pointer`this`

• available inside member functions
• `this` returns the address of an object itself
• `this->` can be used to access members
• `*this` accesses the object itself
``````#include <iostream>
class IntRange {
int l_ = 0;
int r_ = 0;
public:
explicit
IntRange (int l, int r): l_{l}, r_{r} {
if (l_ > r_) std::swap(l_, r_);
}
int left ()  const { return l_; }
// can also use 'this' to access members:
int right () const { return this->r_; }
…
// returns reference to object itself
IntRange& shift (int by) {
l_ += by;
r_ += by;
return *this;
}
IntRange& widen (int by) {
l_ -= by;
r_ += by;
return *this;
}
};

int main () {
IntRange r1 {1,3};

r1.shift(1);
std::cout << '[' << r1.left() <<','<< r1.right() << "]\n";

r1.shift(2).widen(1);
std::cout << '[' << r1.left() <<','<< r1.right() << "]\n";
}``````
``````IntRange r1 {1,3};
r1.shift(1);
r1.shift(2).widen(1);  // chaining possible!``````
``````1 3
2 4
3 7``````

## Forward Declarations of TypesForward DeclarationsDeclarations

``````// forward declaration
class Hub;
class Device {
Hub* hub_;
…
};
class Hub {
std::vector<Device const*> devs_;
…
};`````` In order to define a type, the memory sizes of all its members must be known.

This in turn is only possible if the full definition of all members is known.

However, all pointer types have the same size
⇒ we can just declare the existence of Hub, because Device only needs a pointer to it.

## Avoid Pointers If PossibleTry to Avoid PointersAvoid Pointers!

• value stored in pointer can be any address
• programmer has to make sure pointer target is valid / still exists
``````int* p;  // p not initialized!
*p = 7;  //  UNDEFINED BEHAVIOR``````
``````p = nullptr;
*p = 7;  //  UNDEFINED BEHAVIOR access to nullptr``````
``````{
int x = 8;
p = &x;
``````void swap_values (int* a, int* b) {
``*p = *p * *p + (2 * *p + 1);   // SO MANY STARS!``