KEMBAR78
Pointers & References in C++ | PDF
Pointers & References
Ilio Catallo - info@iliocatallo.it
Outline
β€’ The swap func*on
β€’ Memory & addresses
β€’ Pointers
β€’ References
β€’ Bibliography
The swap func)on
Swapping integers
Assume that we are wri-ng a program in which we o3en need to
swap two integers
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚
β”‚ β–Ό
β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β”
a:β”‚ 2 β”‚int b:β”‚ 5 β”‚int
β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜
β–² β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Idea: wri%ng a swap_int func%on
Invoking swap_int
int main() {
int a = 2;
int b = 5;
// expected result: a = 5, b = 2
swap_int(a, b);
}
How to write the swap_int func.on?
First a(empt
void swap_int(int x, int y) {
int temp = x;
x = y;
y = temp;
}
First a(empt
At swap_int invoca*on:
β€’ The values in a and b get copied into the local variables x and y
β€’ The values in x and y get swapped, but...
β€’ The values in a and b remain untouched
First a(empt
void swap_int(int x, int y) {
...
}
int main() {
int a = 2, b = 5;
swap_int(a, b); // x and y are copies of a and b
// hence, a and b remain unchanged
}
The swap problem
We conclude that it is not possible to write the swap_int
func1on if we pass parameters by value
Memory & addresses
Memory
During the execu-on of a program, data are stored in memory
Memory model
In C++, memory is modeled as a sequence of memory cells, where
each cell is of size 1 byte
β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”
... β”‚ β”‚ β”‚ β”‚ β”‚ ...
β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜
◀─────▢
1 byte
Memory model
Each memory cell is associated with a unique memory address
β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”
... β”‚ β”‚ β”‚ β”‚ β”‚ ...
β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜
0x7345
Memory model
Memory addresses are nothing more than numbers1
β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”
... β”‚ β”‚ β”‚ β”‚ β”‚ ...
β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜
0x7345
1
The 0x prefix is to say that the numbers are given in hexadecimal nota6on
Memory model
Given a cell, its address is obtained as the address of the previous
one plus one
β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”
... β”‚ β”‚ β”‚ β”‚ β”‚ ...
β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜
0x7345 0x7346 0x7347
Memory model
This assures that:
β€’ Every memory cell has its own unique address
β€’ Given a cell, it is possible to compute the address of its
neighborhood
Variable iden+fiers
We usually do not care about where values are stored
int a
◀───────────▢
β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”
... β”‚//////β”‚//////β”‚ β”‚ β”‚ ...
β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜
0x7345 0x7346 0x7347
Variable iden+fiers
We simply manipulate values by referring to the iden%fier of the
corresponding variable
int a = 3;
a = a + 7; // we do not know where `a` is stored in memory
Addresses
However, there might be situa2ons in which we would like to refer
to the in-memory loca-on of a value, rather than to the value itself
The ampersand
In order to manipulate the address of a variable, we prefix the
variable iden8fier with the ampersand symbol &
The ampersand
int a = 5;
std::cout << "value of a: " << a
<< std::endl;
std::cout << "address of a: " << &a
<< std::endl;
The address-of operator
When the ampersand symbol & precedes an expression2
, it plays
the role of a unary operator (the address-of operator)
int a = 7;
std::cout << &a; // conceptually: address_of(a);
2
Note that there exist expressions on which it is not possible to apply the address-of operator
The address-of operator
Note that if a value occupies more than one memory cell, the
address-of operator returns the address of the first memory cell
int a
◀───────────▢
β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”
... β”‚//////β”‚//////β”‚ β”‚ β”‚ ...
β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜
0x7345 0x7346 0x7347
β–²
β”‚
β”‚
&a
A formal defini.on of copy
A copy is a something that is equal to the original but not iden)cal
to it
int a = 7;
int b = a;
a == b; // b is equal to a (they both contain 7)
&a != &b; // b is not a (they live in different memory locations)
Pointers
Storing addresses
Assume that we now want to store the address of a value. We need
a variable where to keep such an address
Storing addresses
int a = 7;
??? address_of_a = &a
What is the type of address_of_a?
Pointer to int
The data type of a variable storing the address of an int is int*.
Such a data type is called pointer to int
int* address_of_a = &a
Pointers
Variables that store addresses are called pointers
It is possible to create pointers to any data type3
, for built-in and
user-defined types alike
3
As long as the data type is at least of size 1 byte
For any data type T, T* is the data type pointer to T
Examples
int* a; // pointer to int
char* b; // pointer to char
float* c; // pointer to float
int** d; // pointer to pointer to int
XKCD
Pointers as type constructors
The * symbol plays the role of a type constructor
A type constructor is a func0on that takes as input some type T
and returns some other type T'
Example
Construc)ng the type "pointer to int"
β€’ Input type: int
β€’ Type constructor: *
β€’ Output type: int*
vector as type constructor
No#ce that also std::vector is a type constructor
vector as type constructor
Indeed, no value can have a type of just std::vector, because
that is not a type per se
Example
Construc)ng the type "vector of double's"
β€’ Input type: double
β€’ Type constructor: std::vector
β€’ Output type: std::vector<double>
Type constructors: a metaphor
Type constructors are like β™­ and β™― in music
Type constructors: a metaphor
You cannot play β™­ or β™― alone
Type constructors: a metaphor
You rather apply β™­ or β™― to a note to obtain a new one
Type constructors: a metaphor
Construc)ng the note Aβ™­
β€’ Input note: A
β€’ Note constructor: β™­
β€’ Output note: Aβ™­
Using pointers
Using pointers
Assume we are storing the address of an integer variable in a
pointer
int* ptr = ... // somehow initialized
What opera*ons can I do on pointers?
Dereferencing
The dereference operator * makes possible to access the content
pointed to by a pointer4
int* ptr = ...
std::cout << "the value pointed to is: " << *ptr;
4
The dereference operator is also called the indirec'on operator
Dereferencing
The expression *ptr reads "the value pointed to by ptr"
int* ptr = ...
std::cout << "the value pointed to is: " << *ptr;
Dereferencing
The dereference operator allows both reading and modifying the
pointed value
int* ptr = ...
*ptr = 7;
int b = 5 + *ptr; // b contains 12
Dereferencing
In other words, the expression *ptr may also appear on the le4-
hand side of the assignment operator
int* ptr = ...
*ptr = 7;
int b = 5 + *ptr; // b contains 12
Same symbol, two meanings
// * is a type constructor
int* ptr_a = &a;
// * is an operator
std::cout << "pointed value: " << *ptr_a;
Trivia 1: What's the output?
int a = 7;
int* ptr = &a;
*ptr = 12;
std::cout << *ptr << std::endl;
std::cout << a << std::endl;
Trivia 2: What's the output?
int a = 7;
int* ptr = &a;
std::cout << &ptr << std::endl;
std::cout << &a << std::endl;
Trivia 3: What's the output?
int a = 7;
int* ptr1 = &a;
int* ptr2 = &a;
std::cout << (ptr1 == ptr2) << std::endl;
std::cout << (&ptr1 == &ptr2) << std::endl;
The "box" metaphor
The "box" metaphor
Just as a vector is a container of many elements, we can say that a
pointer is a container of one element5
5
More formally, the similarity holds because both of them are functors
The "box" metaphor
// we put some values in the container
std::vector<int> v = {1, 2, 3, 4, 5};
// accessing the 3rd element
// in the container
std::cout << v[2];
The "box" metaphor
// we "put" a value in the container
int* p = &a;
// accessing the only element
// in the container
std::cout << *p;
Geek and Poke
The strange case of null pointers
Unini$alized pointers
As with any local variable, it is not possible to foresee the value of
unini6alized pointers
int* ptr; // uninitialized pointer
Unini$alized pointers
The pointer may hold any value. Such a value will be erroneously
intended as the address of a pointed value
int* ptr; // uninitialized pointer
Unini$alized pointers
There does not exist any way to understand whether a pointer is in
fact poin4ng to a valid value
int* ptr; // uninitialized pointer
Null pointers
We would like to ini#alize pointers even in the absence of a
sensible value
int* ptr = ???;
Null pointers
Pointers can be ini-alized to null in order to indicate that the
pointer is currently not poin-ng to any valid value6
int* ptr = nullptr; // since C++11
int* ptr = NULL; // C++03
6
In terms of the box metaphor, this amounts to no4fying that the container is empty
Solving the swap problem
Can we use pointers to write a working version of the swap_int
func4on?
Idea: passing to swap_int the variable addresses (as
opposed to their values)
swap_int with pointers
void swap_int(??? x, ??? y) {
...
}
swap_int with pointers
void swap_int(int* x, int* y) {
...
}
swap_int with pointers
void swap_int(int* x, int* y) {
int temp = *x;
*x = *y;
*y = temp;
}
swap_int with pointers
At swap_int invoca*on:
β€’ The addresses of a and b get copied into the pointers x and y
(which are local to swap_int)
β€’ The values pointed to by x and y (i.e., a and b) get swapped
β€’ At the end of swap_int, x and y get destroyed
How to invoke swap_int?
int main() {
int a = 2;
int b = 5;
swap_int(???, ???);
}
How to invoke swap_int?
int main() {
int a = 2;
int b = 5;
swap_int(&a, &b);
}
Passing by address
If a func(on receives addresses in place of values, we say that
parameter are passed by address
void swap_int(int* x, int* y);
Passing by address
However, since we pass address copies, passing by address is just a
par6cular case of passing by value
void swap_int(int* x, int* y);
Passing by address
Passing by address allows...
β€’ propaga'ng altera'ons in the callee func'on also in the calling
func'on
β€’ rapidly accessing big data, since we copy its address as opposed
to its value
Passing by address
However:
β€’ The code is difficult to read
β€’ We should manage the edge case where we pass nullptr
Passing by address
Upsides/downsides:
β€’ We cannot pass constants or temporary data (they do not have
an address)
References
Iden%fiers
When declaring a variable, we need to specify its data type and
iden*fier
Both these aspects cannot be modified later on in the program
Iden%fier aliases
However, C++ allows introducing addi5onal iden5fiers (aliases) for
a variables any5me in the code
int a = 5;
int& b = a; // b is a new identifier (alias) for a
Iden%fier aliases
b is an alias for a, and its data type is int&
int a = 5;
int& b = a; // b is a new identifier (alias) for a
References
In C++, iden*fier aliases are called references7
int a = 5;
int& b = a; // b is a reference to a
7
Star'ng from C++11, references have been renamed lvalue references
For a data type T, T& is the data type reference to T
Examples
int& a = b; // a is an alias for b (where b is of type int)
float& c = d // c is an alias for d (where d is of type float)
int& e = b; // e is an alias for b, hence an alias for a
int*& f = g; // f is an alias for g (where g is of type int*)
Trivia 1: what's the output?
int a = 5;
int& b = a;
b = 7;
std::cout << a;
Trivia 1: what's the output?
int a = 5;
int& b = a; // b is a new name for a
b = 7;
std::cout << a; // the output will be 7
Aliases are indis+nguishable
Once ini'alized, using either the new or the old iden'fier is a
ma7er of indifference
int a = 5;
int& b = a; // b is a new name for a
// using b is the same as using a (and vice-versa)
std::cout << b;
Aliases are indis+nguishable
That is, the reference is the referent8
int a = 5;
int& b = a; // b is a new name for a
// using b is the same as using a (and vice-versa)
std::cout << b;
8
While holding conceptually, the C++11 specifica8on qualifies the reference and the referent as two different
en88es
& as a type constructor
As with *, also & can be intended as a type constructor
& as a type constructor
Construc)ng the type "reference to float"
β€’ Input type: float
β€’ Type constructor: &
β€’ Output type: float&
Same symbol, two meanings
int a = 5;
// & is a type constructor
int& b = a;
// & is an operator
std::cout << &a;
Solving the swap problem
Can we use references to write a working version of the swap_int
func3on?
Idea: using references as parameters of swap_int
swap_int with references
void swap_int(??? x, ??? y) {
...
}
swap_int with references
void swap_int(int& x, int& y) {
...
}
swap_int with references
void swap_int(int& x, int& y) {
int temp = x;
x = y;
y = temp;
}
swap_int with references
Note that the func,on body is the same as in our first a5empt
void swap_int(int& x, int& y) {
int temp = x;
x = y;
y = temp;
}
How to invoke swap_int?
int main() {
int a = 2;
int b = 5;
swap_int(???, ???);
}
How to invoke swap_int?
int main() {
int a = 2;
int b = 5;
swap_int(a, b);
}
How to invoke swap_int?
Note that we invoke swap_int as any other func3on
int main() {
int a = 2;
int b = 5;
swap_int(a, b);
}
swap_int with references
References allow us to write swap_int in an easy and safe
manner
β€’ Easy: we do not need to use the dereference operator *
everywhere
β€’ Safe: the program will not compile if we try to pass nullptr or
temporary values
Passing by reference
If a func(on receives iden%fier aliases in place of values, we say
that parameter are passed by reference
void swap_int(int& x, int& y);
Gotchas with references
Does it work?
int a = 5;
int& b;
b = a;
References are not assignable
References can be ini#alized, but not (re)-assigned
int a = 5;
int& b; // b is an alias for what?
b = a;
Does it work?
void countdown(int& number) {
std::cout << number << std::endl;
if (number != 0)
countdown(number - 1);
}
The compile-,me error
Here's the error:
error: invalid initialization of non-const reference of
type 'int&' from an rvalue of type 'int'
The compile-,me error
We can safely replace the term rvalue with temporary:
error: invalid initialization of non-const reference of
type 'int&' from a temporary of type 'int'
The compile-,me error
The temporary the error is referring to is number - 1
void countdown(int& number) {
std::cout << number << std::endl;
if (number != 0)
countdown(number - 1);
}
Reference-to-const
The correct version reads:
void countdown(int const& number) {
std::cout << number << std::endl;
if (number != 0)
countdown(number - 1);
}
Reference-to-const
Only constant references can bind to temporary values
void countdown(int const& number) {
std::cout << number << std::endl;
if (number != 0)
countdown(number - 1);
}
Reference-to-const
This is why the input parameter number has to be of type int
const&, rather than int&
void countdown(int const& number) {
std::cout << number << std::endl;
if (number != 0)
countdown(number - 1);
}
Does it work?
double& add(double x, double y) {
double z = x + y;
return z;
}
Dangling references
Although it compiles, the add func2on causes an undefined
behavior
double& add(double x, double y) {
double z = x + y;
return z;
}
Dangling references
This is because we are returning an alias for z, which will be
destroyed as soon as we exit the func7on
double& add(double x, double y) {
double z = x + y;
return z;
}
Dangling references
In other words, the add func0on returns a dangling reference, that
is, a reference for a non-exis0ng object
double& add(double x, double y) {
double z = x + y;
return z;
}
int main() {
double r = add(5, 3);
std::cout << r;
}
Dangling references
Although this seems trivial when presented in contrived examples,
this is an easy mistake to make when wri9ng class' ge;ers and
se;ers
Bibliography
Bibliography
β€’ S. B. Lippman, J. Lajoie, B. E. Moo, C++ Primer (5th Ed.)
β€’ B. Stroustrup, The C++ Programming Language (4th Ed.)
β€’ R. Lafore, Object Oriented Programming in C++ (4th Ed.)
β€’ C++FAQ, SecJon 8

Pointers & References in C++

  • 1.
    Pointers & References IlioCatallo - info@iliocatallo.it
  • 2.
    Outline β€’ The swapfunc*on β€’ Memory & addresses β€’ Pointers β€’ References β€’ Bibliography
  • 3.
  • 4.
    Swapping integers Assume thatwe are wri-ng a program in which we o3en need to swap two integers β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β–Ό β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β” a:β”‚ 2 β”‚int b:β”‚ 5 β”‚int β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β–² β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • 5.
    Idea: wri%ng aswap_int func%on
  • 6.
    Invoking swap_int int main(){ int a = 2; int b = 5; // expected result: a = 5, b = 2 swap_int(a, b); }
  • 7.
    How to writethe swap_int func.on?
  • 8.
    First a(empt void swap_int(intx, int y) { int temp = x; x = y; y = temp; }
  • 9.
    First a(empt At swap_intinvoca*on: β€’ The values in a and b get copied into the local variables x and y β€’ The values in x and y get swapped, but... β€’ The values in a and b remain untouched
  • 10.
    First a(empt void swap_int(intx, int y) { ... } int main() { int a = 2, b = 5; swap_int(a, b); // x and y are copies of a and b // hence, a and b remain unchanged }
  • 11.
    The swap problem Weconclude that it is not possible to write the swap_int func1on if we pass parameters by value
  • 12.
  • 13.
    Memory During the execu-onof a program, data are stored in memory
  • 14.
    Memory model In C++,memory is modeled as a sequence of memory cells, where each cell is of size 1 byte β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β” ... β”‚ β”‚ β”‚ β”‚ β”‚ ... β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜ ◀─────▢ 1 byte
  • 15.
    Memory model Each memorycell is associated with a unique memory address β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β” ... β”‚ β”‚ β”‚ β”‚ β”‚ ... β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜ 0x7345
  • 16.
    Memory model Memory addressesare nothing more than numbers1 β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β” ... β”‚ β”‚ β”‚ β”‚ β”‚ ... β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜ 0x7345 1 The 0x prefix is to say that the numbers are given in hexadecimal nota6on
  • 17.
    Memory model Given acell, its address is obtained as the address of the previous one plus one β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β” ... β”‚ β”‚ β”‚ β”‚ β”‚ ... β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜ 0x7345 0x7346 0x7347
  • 18.
    Memory model This assuresthat: β€’ Every memory cell has its own unique address β€’ Given a cell, it is possible to compute the address of its neighborhood
  • 19.
    Variable iden+fiers We usuallydo not care about where values are stored int a ◀───────────▢ β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β” ... β”‚//////β”‚//////β”‚ β”‚ β”‚ ... β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜ 0x7345 0x7346 0x7347
  • 20.
    Variable iden+fiers We simplymanipulate values by referring to the iden%fier of the corresponding variable int a = 3; a = a + 7; // we do not know where `a` is stored in memory
  • 21.
    Addresses However, there mightbe situa2ons in which we would like to refer to the in-memory loca-on of a value, rather than to the value itself
  • 22.
    The ampersand In orderto manipulate the address of a variable, we prefix the variable iden8fier with the ampersand symbol &
  • 23.
    The ampersand int a= 5; std::cout << "value of a: " << a << std::endl; std::cout << "address of a: " << &a << std::endl;
  • 24.
    The address-of operator Whenthe ampersand symbol & precedes an expression2 , it plays the role of a unary operator (the address-of operator) int a = 7; std::cout << &a; // conceptually: address_of(a); 2 Note that there exist expressions on which it is not possible to apply the address-of operator
  • 25.
    The address-of operator Notethat if a value occupies more than one memory cell, the address-of operator returns the address of the first memory cell int a ◀───────────▢ β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β” ... β”‚//////β”‚//////β”‚ β”‚ β”‚ ... β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜ 0x7345 0x7346 0x7347 β–² β”‚ β”‚ &a
  • 26.
    A formal defini.onof copy A copy is a something that is equal to the original but not iden)cal to it int a = 7; int b = a; a == b; // b is equal to a (they both contain 7) &a != &b; // b is not a (they live in different memory locations)
  • 27.
  • 28.
    Storing addresses Assume thatwe now want to store the address of a value. We need a variable where to keep such an address
  • 29.
    Storing addresses int a= 7; ??? address_of_a = &a
  • 30.
    What is thetype of address_of_a?
  • 31.
    Pointer to int Thedata type of a variable storing the address of an int is int*. Such a data type is called pointer to int int* address_of_a = &a
  • 32.
    Pointers Variables that storeaddresses are called pointers It is possible to create pointers to any data type3 , for built-in and user-defined types alike 3 As long as the data type is at least of size 1 byte
  • 33.
    For any datatype T, T* is the data type pointer to T
  • 34.
    Examples int* a; //pointer to int char* b; // pointer to char float* c; // pointer to float int** d; // pointer to pointer to int
  • 35.
  • 36.
    Pointers as typeconstructors The * symbol plays the role of a type constructor A type constructor is a func0on that takes as input some type T and returns some other type T'
  • 37.
    Example Construc)ng the type"pointer to int" β€’ Input type: int β€’ Type constructor: * β€’ Output type: int*
  • 38.
    vector as typeconstructor No#ce that also std::vector is a type constructor
  • 39.
    vector as typeconstructor Indeed, no value can have a type of just std::vector, because that is not a type per se
  • 40.
    Example Construc)ng the type"vector of double's" β€’ Input type: double β€’ Type constructor: std::vector β€’ Output type: std::vector<double>
  • 41.
    Type constructors: ametaphor Type constructors are like β™­ and β™― in music
  • 42.
    Type constructors: ametaphor You cannot play β™­ or β™― alone
  • 43.
    Type constructors: ametaphor You rather apply β™­ or β™― to a note to obtain a new one
  • 44.
    Type constructors: ametaphor Construc)ng the note Aβ™­ β€’ Input note: A β€’ Note constructor: β™­ β€’ Output note: Aβ™­
  • 45.
  • 46.
    Using pointers Assume weare storing the address of an integer variable in a pointer int* ptr = ... // somehow initialized
  • 47.
    What opera*ons canI do on pointers?
  • 48.
    Dereferencing The dereference operator* makes possible to access the content pointed to by a pointer4 int* ptr = ... std::cout << "the value pointed to is: " << *ptr; 4 The dereference operator is also called the indirec'on operator
  • 49.
    Dereferencing The expression *ptrreads "the value pointed to by ptr" int* ptr = ... std::cout << "the value pointed to is: " << *ptr;
  • 50.
    Dereferencing The dereference operatorallows both reading and modifying the pointed value int* ptr = ... *ptr = 7; int b = 5 + *ptr; // b contains 12
  • 51.
    Dereferencing In other words,the expression *ptr may also appear on the le4- hand side of the assignment operator int* ptr = ... *ptr = 7; int b = 5 + *ptr; // b contains 12
  • 52.
    Same symbol, twomeanings // * is a type constructor int* ptr_a = &a; // * is an operator std::cout << "pointed value: " << *ptr_a;
  • 53.
    Trivia 1: What'sthe output? int a = 7; int* ptr = &a; *ptr = 12; std::cout << *ptr << std::endl; std::cout << a << std::endl;
  • 54.
    Trivia 2: What'sthe output? int a = 7; int* ptr = &a; std::cout << &ptr << std::endl; std::cout << &a << std::endl;
  • 55.
    Trivia 3: What'sthe output? int a = 7; int* ptr1 = &a; int* ptr2 = &a; std::cout << (ptr1 == ptr2) << std::endl; std::cout << (&ptr1 == &ptr2) << std::endl;
  • 56.
  • 57.
    The "box" metaphor Justas a vector is a container of many elements, we can say that a pointer is a container of one element5 5 More formally, the similarity holds because both of them are functors
  • 58.
    The "box" metaphor //we put some values in the container std::vector<int> v = {1, 2, 3, 4, 5}; // accessing the 3rd element // in the container std::cout << v[2];
  • 59.
    The "box" metaphor //we "put" a value in the container int* p = &a; // accessing the only element // in the container std::cout << *p;
  • 60.
  • 61.
    The strange caseof null pointers
  • 62.
    Unini$alized pointers As withany local variable, it is not possible to foresee the value of unini6alized pointers int* ptr; // uninitialized pointer
  • 63.
    Unini$alized pointers The pointermay hold any value. Such a value will be erroneously intended as the address of a pointed value int* ptr; // uninitialized pointer
  • 64.
    Unini$alized pointers There doesnot exist any way to understand whether a pointer is in fact poin4ng to a valid value int* ptr; // uninitialized pointer
  • 65.
    Null pointers We wouldlike to ini#alize pointers even in the absence of a sensible value int* ptr = ???;
  • 66.
    Null pointers Pointers canbe ini-alized to null in order to indicate that the pointer is currently not poin-ng to any valid value6 int* ptr = nullptr; // since C++11 int* ptr = NULL; // C++03 6 In terms of the box metaphor, this amounts to no4fying that the container is empty
  • 67.
    Solving the swapproblem Can we use pointers to write a working version of the swap_int func4on?
  • 68.
    Idea: passing toswap_int the variable addresses (as opposed to their values)
  • 69.
    swap_int with pointers voidswap_int(??? x, ??? y) { ... }
  • 70.
    swap_int with pointers voidswap_int(int* x, int* y) { ... }
  • 71.
    swap_int with pointers voidswap_int(int* x, int* y) { int temp = *x; *x = *y; *y = temp; }
  • 72.
    swap_int with pointers Atswap_int invoca*on: β€’ The addresses of a and b get copied into the pointers x and y (which are local to swap_int) β€’ The values pointed to by x and y (i.e., a and b) get swapped β€’ At the end of swap_int, x and y get destroyed
  • 73.
    How to invokeswap_int? int main() { int a = 2; int b = 5; swap_int(???, ???); }
  • 74.
    How to invokeswap_int? int main() { int a = 2; int b = 5; swap_int(&a, &b); }
  • 75.
    Passing by address Ifa func(on receives addresses in place of values, we say that parameter are passed by address void swap_int(int* x, int* y);
  • 76.
    Passing by address However,since we pass address copies, passing by address is just a par6cular case of passing by value void swap_int(int* x, int* y);
  • 77.
    Passing by address Passingby address allows... β€’ propaga'ng altera'ons in the callee func'on also in the calling func'on β€’ rapidly accessing big data, since we copy its address as opposed to its value
  • 78.
    Passing by address However: β€’The code is difficult to read β€’ We should manage the edge case where we pass nullptr
  • 79.
    Passing by address Upsides/downsides: β€’We cannot pass constants or temporary data (they do not have an address)
  • 80.
  • 81.
    Iden%fiers When declaring avariable, we need to specify its data type and iden*fier Both these aspects cannot be modified later on in the program
  • 82.
    Iden%fier aliases However, C++allows introducing addi5onal iden5fiers (aliases) for a variables any5me in the code int a = 5; int& b = a; // b is a new identifier (alias) for a
  • 83.
    Iden%fier aliases b isan alias for a, and its data type is int& int a = 5; int& b = a; // b is a new identifier (alias) for a
  • 84.
    References In C++, iden*fieraliases are called references7 int a = 5; int& b = a; // b is a reference to a 7 Star'ng from C++11, references have been renamed lvalue references
  • 85.
    For a datatype T, T& is the data type reference to T
  • 86.
    Examples int& a =b; // a is an alias for b (where b is of type int) float& c = d // c is an alias for d (where d is of type float) int& e = b; // e is an alias for b, hence an alias for a int*& f = g; // f is an alias for g (where g is of type int*)
  • 87.
    Trivia 1: what'sthe output? int a = 5; int& b = a; b = 7; std::cout << a;
  • 88.
    Trivia 1: what'sthe output? int a = 5; int& b = a; // b is a new name for a b = 7; std::cout << a; // the output will be 7
  • 89.
    Aliases are indis+nguishable Onceini'alized, using either the new or the old iden'fier is a ma7er of indifference int a = 5; int& b = a; // b is a new name for a // using b is the same as using a (and vice-versa) std::cout << b;
  • 90.
    Aliases are indis+nguishable Thatis, the reference is the referent8 int a = 5; int& b = a; // b is a new name for a // using b is the same as using a (and vice-versa) std::cout << b; 8 While holding conceptually, the C++11 specifica8on qualifies the reference and the referent as two different en88es
  • 91.
    & as atype constructor As with *, also & can be intended as a type constructor
  • 92.
    & as atype constructor Construc)ng the type "reference to float" β€’ Input type: float β€’ Type constructor: & β€’ Output type: float&
  • 93.
    Same symbol, twomeanings int a = 5; // & is a type constructor int& b = a; // & is an operator std::cout << &a;
  • 94.
    Solving the swapproblem Can we use references to write a working version of the swap_int func3on?
  • 95.
    Idea: using referencesas parameters of swap_int
  • 96.
    swap_int with references voidswap_int(??? x, ??? y) { ... }
  • 97.
    swap_int with references voidswap_int(int& x, int& y) { ... }
  • 98.
    swap_int with references voidswap_int(int& x, int& y) { int temp = x; x = y; y = temp; }
  • 99.
    swap_int with references Notethat the func,on body is the same as in our first a5empt void swap_int(int& x, int& y) { int temp = x; x = y; y = temp; }
  • 100.
    How to invokeswap_int? int main() { int a = 2; int b = 5; swap_int(???, ???); }
  • 101.
    How to invokeswap_int? int main() { int a = 2; int b = 5; swap_int(a, b); }
  • 102.
    How to invokeswap_int? Note that we invoke swap_int as any other func3on int main() { int a = 2; int b = 5; swap_int(a, b); }
  • 103.
    swap_int with references Referencesallow us to write swap_int in an easy and safe manner β€’ Easy: we do not need to use the dereference operator * everywhere β€’ Safe: the program will not compile if we try to pass nullptr or temporary values
  • 104.
    Passing by reference Ifa func(on receives iden%fier aliases in place of values, we say that parameter are passed by reference void swap_int(int& x, int& y);
  • 105.
  • 106.
    Does it work? inta = 5; int& b; b = a;
  • 107.
    References are notassignable References can be ini#alized, but not (re)-assigned int a = 5; int& b; // b is an alias for what? b = a;
  • 108.
    Does it work? voidcountdown(int& number) { std::cout << number << std::endl; if (number != 0) countdown(number - 1); }
  • 109.
    The compile-,me error Here'sthe error: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
  • 110.
    The compile-,me error Wecan safely replace the term rvalue with temporary: error: invalid initialization of non-const reference of type 'int&' from a temporary of type 'int'
  • 111.
    The compile-,me error Thetemporary the error is referring to is number - 1 void countdown(int& number) { std::cout << number << std::endl; if (number != 0) countdown(number - 1); }
  • 112.
    Reference-to-const The correct versionreads: void countdown(int const& number) { std::cout << number << std::endl; if (number != 0) countdown(number - 1); }
  • 113.
    Reference-to-const Only constant referencescan bind to temporary values void countdown(int const& number) { std::cout << number << std::endl; if (number != 0) countdown(number - 1); }
  • 114.
    Reference-to-const This is whythe input parameter number has to be of type int const&, rather than int& void countdown(int const& number) { std::cout << number << std::endl; if (number != 0) countdown(number - 1); }
  • 115.
    Does it work? double&add(double x, double y) { double z = x + y; return z; }
  • 116.
    Dangling references Although itcompiles, the add func2on causes an undefined behavior double& add(double x, double y) { double z = x + y; return z; }
  • 117.
    Dangling references This isbecause we are returning an alias for z, which will be destroyed as soon as we exit the func7on double& add(double x, double y) { double z = x + y; return z; }
  • 118.
    Dangling references In otherwords, the add func0on returns a dangling reference, that is, a reference for a non-exis0ng object double& add(double x, double y) { double z = x + y; return z; } int main() { double r = add(5, 3); std::cout << r; }
  • 119.
    Dangling references Although thisseems trivial when presented in contrived examples, this is an easy mistake to make when wri9ng class' ge;ers and se;ers
  • 120.
  • 121.
    Bibliography β€’ S. B.Lippman, J. Lajoie, B. E. Moo, C++ Primer (5th Ed.) β€’ B. Stroustrup, The C++ Programming Language (4th Ed.) β€’ R. Lafore, Object Oriented Programming in C++ (4th Ed.) β€’ C++FAQ, SecJon 8