CS253: Software Development with C++

Fall 2022

Pointers

Show Lecture.Pointers as a slide show.

CS253 Pointers

Philosophy


Pointers

Ampersand (&)

int earth = 616;
int *p = &earth;  // p contains the address of earth
int &r = earth;   // r is a reference (alias) to earth
cout << earth << ' ' << *p << ' ' << r << '\n';
earth = 1;
cout << earth << ' ' << *p << ' ' << r << '\n';
*p = 2;
cout << earth << ' ' << *p << ' ' << r << '\n';
r = 3;
cout << earth << ' ' << *p << ' ' << r << '\n';
616 616 616
1 1 1
2 2 2
3 3 3

An example

int a[] = {11, 22, 33, 44, 55};
int *p = &a[2];
cout << *p << '\n';
p += 2;
cout << *p << '\n';
33
55

Bad examples

int a[] = {11, 22, 33, 44, 55};
int *p = &a[2], n;
n = p;  // 🦡
c.cc:3: error: invalid conversion from ‘int*’ to ‘int’
int a[] = {11, 22, 33, 44, 55};
int *p = &a[2], n;
p = n;  // 🦡
c.cc:3: error: invalid conversion from ‘int’ to ‘int*’

Declaration style

These are all equivalent, since spaces matter little:

int *a;
int* b;
int*c;
int   *   d;
int
*
e;
What types are g and h?

g is an int *, but h is simply an int.

int* g, h;  // 🦡

That’s why I do this:

int *i, j;

Another example

int a[] = {11, 22, 33, 44, 55};
int *p = a;
cout << *p << '\n';
*p += 8;
cout << *p << '\n';
p += 2;
cout << *p << '\n';
11
19
33
int a[5] = {11, 22, 33, 44, 55};
cout << a     << '\n'
     << &a[0] << '\n';
0x7ffea39a4cb0
0x7ffea39a4cb0

Null pointers

References

A reference is like a pointer, with auto-indirection.

int a[] = {11, 22, 33, 44, 55};
int &r = a[2];
cout << r << '\n';
r += 2;
cout << r << '\n';
33
35

A common use

void f1(string s) {        // call by value
    cout << s << '\n';
}

void f2(const string &s) { // call by const reference
    cout << s << '\n';
}

int main() {
    string taunt = "Your mother is a hamster.";
    f1(taunt);
    f2(taunt);
    return 0;
}
Your mother is a hamster.
Your mother is a hamster.

Use of const

DeclarationExplanation
int *a;movable pointer to writable int
const int *b;movable pointer to read-only int
int *const c;unmovable pointer to writable int
const int *const d;unmovable pointer to read-only int
int &e = …;read/write reference an int
const int &f = …;read-only reference to an int

Examples

Declare a pointer to constant integers. We can change the pointer, but not the integers.

int alpha[] = {11,22,33,44,55};
const int *p = alpha;
p += 2;
cout << *p;
33
int beta[] = {11,22,33,44,55};
const int *p = beta;
*p = 42;  // 🦡
c.cc:3: error: assignment of read-only location ‘* p’

Examples

Declare a constant pointer to integers. We can change the the integers, but not the pointer.

int gamma[] = {11,22,33,44,55};
int *const p = gamma;
*p = 42;
cout << *p;
42
int delta[] = {11,22,33,44,55};
int *const p = delta;
p++;  // 🦡
c.cc:3: error: increment of read-only variable ‘p’

Dot or pointer?

Examples

vector<bool> v(42, true);   // v is a vector of bool

cout << v.size() << '\n';
42

Here, we use a dot, because v is an object, not a pointer.

Examples

vector<bool> v(42, true);    // v is a vector of bool
vector<bool> *p = &v;        // p is a pointer to vector of bool

cout << (*p).size() << '\n'  // 🦡
     << p->size()   << '\n';
42
42

Two ways (one ugly, one not) of doing the same thing.

You can’t just use *p.size() because the precedence is wrong; it parses as *(p.size()) instead of (*p).size().

Examples

vector<bool> v(42, true);   // v is a vector of bool
vector<bool> &r = v;        // r is a reference to a vector of bool

cout << r.size() << '\n';
42

Array Name is a Pointer

The name of an array is equivalent to a pointer to its first element.

Write the data via the first pointer, and read it from the second pointer.

double alpha[20];
double *a = alpha;
double *b = &alpha[0];
*a = 123.456;
cout << *b << '\n';
if (a == b)
    cout << "Pointers are equal.\n";
123.456
Pointers are equal.

Arrays & Pointers

You can think of there being two sorts of arrays.

Stack-Based Array Example

The array alpha is on the stack.

int alpha[10];
for (int i=0; i<10; i++)
    alpha[i] = i*i;
for (int i=0; i<10; i++)
    cout << alpha[i] << ' ';
cout << '\n';
cout << "size of alpha: " << sizeof(alpha) << '\n';
0 1 4 9 16 25 36 49 64 81 
size of alpha: 40

This is the best way. The array is on the stack, and goes away when it falls out of scope.

Array Example

The pointer beta is on the stack, pointing to ten ints of dynamic memory.

int *beta = new int[10];
for (int i=0; i<10; i++)
    beta[i] = i*i;
for (int i=0; i<10; i++)
    cout << beta[i] << ' ';
cout << '\n';
cout << "size of beta: " << sizeof(beta) << '\n';
delete[] beta;
0 1 4 9 16 25 36 49 64 81 
size of beta: 8

This is the not as good. If you don’t use delete[], due to an early return from a function, an exception, or just forgetting, then the memory is leaked.

Character-Based Example

const char a[] = "abcdefghijklm";
const char *b  = "nopqrstuvwxyz";
cout << a << ' ' << b << '\n'
     << sizeof(a) << ' ' << sizeof(b) << '\n';
abcdefghijklm nopqrstuvwxyz
14 8

sizeof() returns the size of a thing in bytes. a contains thirteen characters and '\0'; b is simply a pointer, with an implementation-defined size.

Pointer Arithmetic

Pointer Addition

Adding an integer to a pointer moves it forward that many items:

int data[] = {11,22,33,44,55,66,77,88,99};
int *p = data;      // p points to 11
p += 3;             // p now points to 44
cout << *p << '\n';
44

Let’s look at the pointers:

int data[] = {11,22,33,44,55,66,77,88,99};
int *p = data;
cout << p << '\n';
p += 3;
cout << p << '\n';
0x7ffee6f23b80
0x7ffee6f23b8c

We incremented p by 3 ints worth. The actual amount depends on the size of an int, generally 4 bytes.

Pointer Subtraction

Subtracting an integer from a pointer moves it backward that many items:

int data[] = {11,22,33,44,55,66,77,88,99};
int *p = data+7;    // p points to 88
cout << *p << '\n';
p = p - 1;          // p now points to 77
cout << *p << '\n';
88
77

Pointer Subtraction

Subtracting two pointers yields the number of items, not the number of bytes.

int data[20];
int *a = &data[2], *b = &data[9];
cout << b-a << '\n';    // b is 7 past a
7

More Complicated

Of course, you can have a pointer to any type.

// pointer to pointer to int:
int **p;

// pointer to pointer to pointer to pointer to pointer to int
int *****q;

// one hundred pointers to ints:
int *a[100];

// pointer to a function that takes a float & a bool and returns an int
int (*fn)(float, bool);