CS253: Software Development with C++

Fall 2021

Pointers

Show Lecture.Pointers as a slide show.

CS253 Pointers

Philosophy

Pointers

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';
0x7ffc159ecaa0
0x7ffc159ecaa0

Null pointers

int xyz, *p = &xyz;
if (p)
    cout << "Hello\n";
Hello

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 *anon-const pointer to non-const ints
const int *bnon-const pointer to const ints
int *const cconst pointer to non-const ints
const int *const dconst pointer to const ints
int &e = …reference a non-const int
const int &f = …reference to a const 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.

Pointer Arithmetic

Pointer Addition

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';
0x7fff466694b0
0x7fff466694bc

Pointer Subtraction

Similarly, subtracting pointers yields the number of items, not the number of bytes.

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

No, the other kind of pointer subtraction:

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