Week 02 / 14 Object-Oriented Programming
Review - pointers and dynamic memory
Pointers and pointer arithmetic, const with pointers, arrays and matrices, reading and writing with cin and cout, new and delete, memory leaks and dangling pointers.
- Use the address-of and dereference operators and nullptr correctly
- Explain what p + 1 means and why p[i] is the same as *(p + i)
- Tell a pointer to const, a const pointer and a const pointer to const apart
- Work with static arrays and matrices and pass them to functions
- Allocate and release memory with new / delete and new[] / delete[]
- Recognise a memory leak and a dangling pointer, and say who owns a block of memory
Pointers: address and value
A pointer is a variable that holds an address. Two operators do all the work:
&agives the address ofa;*pgives the value stored at the address inp.
int a = 5;
int* p = &a; // p holds the address of a
*p = 8; // writes into a
int* q = nullptr; // points nowhere, on purpose
A pointer that does not point anywhere yet should be nullptr. Never dereference it: *q stops the program.
Pointer arithmetic
Adding 1 to a pointer moves it to the next element, not the next byte. The compiler multiplies by the size of the type for you. That is why p[i] and *(p + i) are the same thing.
#include <iostream>
using namespace std;
int main() {
int v[5] = {10, 20, 30, 40, 50};
int* p = v;
p += 2;
cout << *p << " " << *(p + 1) << " " << p[-1] << endl;
cout << p - v;
} Subtracting two pointers into the same array gives the number of elements between them. Comparing or subtracting pointers into different arrays has no meaning.
const with pointers
Read the declaration from right to left. const applies to what is directly to its left (or to the right, if it is first).
| Declaration | Change *p? | Change p? |
|---|---|---|
const int* p (pointer to const) | No | Yes |
int* const p (const pointer) | Yes | No |
const int* const p | No | No |
int a = 1, b = 2;
const int* p1 = &a; p1 = &b; // OK *p1 = 3; // error
int* const p2 = &a; *p2 = 5; // OK p2 = &b; // error
const int* const p3 = &a; // *p3 = 3; and p3 = &b; are both errors
Arrays and matrices
The name of a static array behaves like a const pointer to its first element: v[i] works, v = other does not compile. When you pass an array to a function, only the address is copied, so the size must travel as a separate parameter:
int sumArray(const int v[], int n); // same as const int* v
A matrix int m[2][3] is stored row by row, as 6 consecutive ints.
#include <iostream>
using namespace std;
int main() {
int m[2][3] = {{1, 2, 3}, {4, 5, 6}};
int s = 0;
for (int i = 0; i < 2; i++) s += m[i][2];
cout << s << " " << m[1][0];
} cin, cout and new[]
When the size is known only at runtime, allocate on the heap with new[] and release with delete[]. Run it, then try a different input.
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int* v = new int[n];
for (int i = 0; i < n; i++) cin >> v[i];
int s = 0;
for (int i = 0; i < n; i++) s += v[i];
cout << "sum: " << s << endl; // expected for input 4 7 9 10 6: sum: 32
delete[] v;
v = nullptr;
}Input (stdin)
new / delete and new[] / delete[]
Every allocation has exactly one matching release:
| Allocate | Release |
|---|---|
new int(7) | delete p; |
new int[n] | delete[] p; |
malloc(...) | free(p); |
Mixing them (delete on a new[] block, free on a new block) is undefined behaviour. So is releasing the same block twice.
int* v = new int[3]{7, 9, 10}; - v is on the stack, the three ints are on the heap.
Step 1/3
Memory leaks
A leak is a heap block whose address you no longer have. Nobody can release it.
int* p = new int[10];
p = new int[10]; // the first block is lost: leak
void f() {
int* t = new int[3];
} // t disappears, the block stays: leak
Before you write new, decide who owns the block, that is, who will call delete. If a function returns a pointer to new memory, the caller owns it and the comment above the function should say so.
Dangling pointers
A dangling pointer holds an address where there is no longer a valid object. Two common ways to get one:
int* createValue() {
int value = 10;
return &value; // wrong: value dies when the function returns
}
int* p = new int(5);
delete p;
*p = 6; // wrong: the block was released
g++ warns about the first one (address of local variable returned). Reading through a dangling pointer is undefined behaviour: it may print the old value, garbage, or crash.
Check yourself
Score: 0/7