Show Lecture.Pointers as a slide show.
CS253 Pointers
Pointers
- Java has references.
- C++ has references.
- C++ also has pointers.
- They are similar to, but different than, references.
An example
int a[] = {11, 22, 33, 44, 55};
int *p = &a[2];
cout << *p << '\n';
p += 2;
cout << *p << '\n';
33
55
a
is an array of int
s
a[2]
is an int
&a[2]
is an int *
, or the address of an int
p
is an int *
, or a pointer to an int
*p
is an int
A bad example
int a[] = {11, 22, 33, 44, 55};
int *p = &a[2], n;
n = p;
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;
However, consider this. What types are e
and f
?
int* e, f;
That’s why I do this:
int *g, h;
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
a
is an array of int
s
a
is equivalent to &a[0]
Null pointers
- A pointer that is intended to not point to anything useful
is a null pointer. It can be represented several ways:
nullptr
: modern C++ way to do it
NULL
: old-fashioned C way to do it
0
: how the implementation actually works. Do not do this.
- A null pointer is false, in a boolean context,
and a non-null pointer is true.
int xyz, *p = &xyz;
if (p)
cout << "Hello\n";
Hello
- An uninitialized pointer is just that, uninitialized.
- If you want it to be null, say so:
int *p = nullptr;
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
- Think of a reference as an alias.
- A reference, once set, cannot be “re-seated”.
- Pointers can change, but references can’t.
- Trying to change a reference only changes the value of the variable it
refers to; the reference still refers to the same variable afterward.
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 filename = "/etc/group";
f1(filename);
f2(filename);
return 0;
}
/etc/group
/etc/group
- Call by reference: fast; call by value: slow (for big things).
- Use
const
so that f2
can’t change the argument.
Use of const
Declaration | Explanation |
int *a | non-const pointer to non-const int s |
const int *b | non-const pointer to const int s |
int *const c | const pointer to non-const int s |
const int *const d | const pointer to const int s |
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?
- Should I use
a->b()
, or a.b()
?
- If
a
is a pointer to an object, use: a->b()
- If
a
is an object, use: a.b()
- If
a
is a reference, then it’s an alias for something--use the
rules for whatever it refers to.
- You can overload
->
for objects—we’ll learn about that later.
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.
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
r
is a reference to an object.
- We use dot with an object.
- Therefore, use dot with
r
.
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.
- One is where the entire array is local to the function.
- The other is where only a pointer to the data is local
to the function. The data that it points to lives elsewhere.
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
Array Example
The pointer beta
is on the stack,
pointing to ten int
s 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
Character-Based Example
const char a[] = "abcdefghijklm";
const char *b = "nopqrstuvwxyz";
cout << a << ' ' << b << '\n'
<< sizeof(a) << ' ' << sizeof(b) << '\n';
abcdefghijklm nopqrstuvwxyz
14 8
Pointer Arithmetic
- What does it mean to increment a pointer?
- What does
p++
, or ++p
, or p+=1
, or p=p+1
do?
- It does not increment the pointer by one byte.
- It does not increment the pointer by one byte.
- It does not increment the pointer by one byte.
- Instead, it increments the pointer by the size of the objects
that it points to. This is why a pointer is
int *
or
string *
, and not just *
.
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';
0x7fff94ba29b0
0x7fff94ba29bc
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
p--; // p now points to 77
cout << *p << '\n';
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);