Topics to Cover

What is a pointer?

We already discussed some of these topics, but pointers are very important, so we want to make sure everyone is comfortable with them.

We have stressed so far that every expression has a type and a value.

Pointer type

Consider the following code snippet:

int n = 3;
cout << &n << endl; // what is the type of &n?
We know that the value of &n is the address of variable n. However, we haven't discussed that type of &n until now. The type of &n is int*. Following the same idea, we can consider the following types as well:

double x = 1.0;
char c = 'a';
string s = "hello";
bool b = false;

cout << &x << endl; // &x is of type double*
cout << &c << endl; // &c is of type char*
cout << &s << endl; // &s is of type string*
cout << &b << endl; // &b is of type bool*
expression type
n int
&n int*
x double
&x double*
c char
&c char*
b bool
&b bool*
The table on the right lists the type of each expression.

In general:

Types with * at the end (i.e., types for representing an address) are called pointer types.

Declaring a pointer variable

Since int* is a type, we can declare a variable of this type as follows:

int* p;

Value of a pointer variable: address

Needless to say, a possible value of p is a memory address.

int n = 1;
int* p;
p = &n;  // This assignment makes sense: typeof(p) = int* = typeof(&n)

Dereference: accessing an object given an address

At this moment, you may ask this natural question:

What's the point of having a variable holding an address?
Is there any useful way to take advantage of this address?
The answer is yes!

Indeed, if you have an address, you can access an object at that address in memory. We call this operation is "dereference". The deference operator is *.

In a sense, we have two operators having the opposite direction from each other. In particular:

Consider the following code snippet:

int n = 10;
int* p;
p = &n; 
cout << *p << endl;
expressionoperator how it works type value
&naddress-of & object → address int* e.g., 0x7fffe5aec3c4
*pdereference * address → object int 10

Check your understanding


int a = 10, b = 7;
int* p = &b; 
*p = 5;
cout << a << " " 
     << b << endl;
What will be the output of code on the right?
Answer:
10 5
Explanation: Note that p points to b (i.e. holds the address of b). Therefore, *p is really b, which means that *p = 5 will change the value of b to 5.
The picture shows that there are two ways to access an object:
  • by using the name of a variable (i.e., using expression b).
  • by using the address of a variable (i.e., using expression *p).

Using this diagram, you can remember the deference operation *p as follows:

Follow the arrow!
  • Note that *p works exactly the same as p[0].
  • When a pointer points to a single object rather than an array, *p gives better readibility of your code.


Dereference (i.e., *) and indexing (i.e., [])

  • Since p points to the first element of the array, *p is actually the same as p[0].
  • Applying the same logic, it also holds that p[i] is the same as *(p+i).
p[i] is equivalent to *(p+i)

Other Questions

Consider the following program mem.cpp.

// mem.cpp
 1 #include <iostream>
 2 #include <string>
 3 using namespace std;
 4
 5 int main()
 6 {
 7   int a = 10, b = 7;
 8   int* p;
 9
10   cout << "&a=" << &a << ", &b=" << &b << ", &p=" << &p << endl;
11   cout << "a=" << a << ", b=" << b << endl;
12
13   uint64_t addr;          // uint64_t: 64-bit unsigned integer 
14   cin >> hex >> addr;     // hex: read in the hexadecimal format
15   p = (int*) addr;        // now p contains the address from the terminal
16 
17   cout << "*p=" << *p << endl;
18   return 0;
19 }
A sample run up to line 13 is given below:
&a=0x7ffff83497e0, &b=0x7ffff83497e4, &p=0x7ffff83497e8
a=10, b=7

Question 1

  1. Give a memory diagram, when the user input from the terminal at line 14 is 0x7ffff83497e0.

    Answer: (Make sure you can draw the following diagram by youself).

     address                value                    (variables)
    ...
    0x7ffff83497e0            10              a
    0x7ffff83497e4            7               b
    0x7ffff83497e8       0x7ffff83497e0       p
    ...
    
  2. What will be the output from line 17? Answer:
    10
    Note: In this case, *p corresponds to an int object at address 0x7ffff83497e0. Well, the int object at address 0x7ffff83497e0 is actually the variable a.

Question 2

  • Suppose that the user input at line 14 is now 0x7ffff83497e4. What will be the output?

    Answer: (Drag your mouse)

    7
  • Suppose that the user input at line 14 is now 0x0. What will be the output?

    Answer: (Drag your mouse)

    The program crashes with the segmentation fault. The
    memory slot at address 0 is not allowed to access.

Heads up! Different meanings for the same symbols

Just as a heads up, now the* and & symbols mean different things in different contexts.
operator in a variable declaration in an expression
* declaring a pointer type
int* p;
dereference
(*p) = 3;
& address-of
int* p;
p = &x;

Usage of operator*

The symbol * has three meanings: multiplication, pointer-declaration, and dereference. How do you know which? Context!

int* p;            // this is a declaration
                   // * means pointer declaration

*p = 7;            // *p is an expression;  the*  is used as a unary rather than binary operator
                   // * means dereference

cout << 3*7;       // 3*7 is an expression, and the*  is used as a binary operator
                   // *  means multiplication

Rule of thumb

It's also important to think about what these operators have to do with types. Generally,

Questions

Drag your mouse for the answers.

char* S;
double** A;
int n;
expressiontype
*Schar
&Schar**
*Adouble*
&Adouble***
*nerror!
&nint*