Topics to Cover
- Address of operator and pointer types.
- What is the deference operation?
- Can you draw pictorial diagrams for expressions containing pointers?
- Can you identify the type of expressions containing pointers?
- How are address-of and dereference operators different?
What is a pointer?
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;
|
| expression | operator | how it works | type | value
| | &n | address-of & | object → address | int* | e.g., 0x7fffe5aec3c4
| | *p | dereference * | 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.
|
Other Questions
Consider the following program mem.cpp.
// mem.cpp
0 #include <iostream>
1 #include <string>
2 #include <cstdint>
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 is not a variable!
15 p = (int*) addr; // now p contains the address from the terminal
16
17 cout << "*p=" << *p << endl;
18 return 0;
19 }
|
|
Note: In the left code, hex is a
special manipulator that changes the formatting of numbers for cin/cout. It is not a
variable!
For example, see the code below:
#include <iostream>
int main()
{
std::cout << std::hex;
std::cout << 10 << std::endl;
std::cout << 17 << std::endl;
return 0;
}
The above code will output
a
11
instead of printing 10 and 17.
|
A sample run up to line 13 is given below:
&a=0x7ffff83497e0, &b=0x7ffff83497e4, &p=0x7ffff83497e8
a=10, b=7
Question 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
...
- 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
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,
- In an expression, the address-of operator & adds one star to a type.
For example, if x is of type int, then &x is of type int*;
if p is of type int*, then &p is of type int**.
- In an expression, the dereference operator* removes one star from a type.
For example, if p is of type int*, then *p is of type int;
if pp is of type int**, then *pp is of type int*;
Questions
Drag your mouse for the answers.
char* S;
double** A;
int n;
|
| expression | type |
| *S | char |
| &S | char** |
| *A | double* |
| &A | double*** |
| *n | error! |
| &n | int* |
|