Skip to content

Pointers

  • Memory address
  • Pointer
  • Pointer type notation
  • Getting the address of a variable
  • Writing to and reading from an address using dereference (dereferencing)
  • Pointer size vs. the size of what it points to
  • Why pointers with different element sizes are incompatible

Explain what will happen in the following code snippets. Also run the snippets to verify that your reasoning is correct.

When main is not shown, place the code in a typical main file:

#include <iostream>
#include <cstdint>
int main()
{
// сюда
}
int a{ 5 };
int* b{ &a }; // int* b = &a;
std::cout << b;
std::cout << std::endl;
Answer:

int* b{ &a } stores the address of the variable a in b. Next, the address stored in b is printed. It is not the value at the address stored in b (that would be written as *b), but the address itself.

What is the type of variable b?

Variable-definition syntax consists of:

  1. a type;
  2. a variable name;
  3. optionally, an initialization.

int* b{ &a }; — here:

  1. int* is the type;
  2. b is the variable name;
  3. { &a } is the initialization.

The type of b is not int, but int*!

What is the type of expression &a?

& is an operator that obtains the address of a variable. & applies not to the value of a, but to the variable a itself.

The type of &a is int*. At compile time, this type indicates that the resulting address is specifically the address of a variable of type int, and not of some other type.

& can give the address of any object, but that will be covered in lab 3.

Is something like this allowed?

int a;
int* b{ &a };
std::cout << b;
std::cout << std::endl;
Answer:

You can take the address of an uninitialized variable. It will be printed as an ordinary address.

Reading the value at that address would not be allowed.

int a { 1 };
int* b{ &a };
*b = 2;
std::cout << a;
std::cout << std::endl;
Answer:

The address of the variable a was stored in b.

In the line *b = 2, *b lets us refer to the variable located at the address in b, that is, to a.

*b = 2 -> a = 2 writes 2 into a.

Is something like this allowed?

int* b{ 32 };
std::cout << *b;
std::cout << std::endl;
Answer:

No. An address cannot be set directly like this; it must be obtained using the & operator on an object (for example, a variable).

int a{ 5 };
int b{ *(&a) };
std::cout << b;
std::cout << std::endl;
Correct answer:

Evaluating the expression *(&a):

  • &a becomes the address of variable a (say, 32).
  • *32 follows the address, allowing access to variable a.
  • When a is used as an expression, it yields the value 5.

*(&a) -> *32 -> a -> 5

int a{ 5 };
int* b{ &a };
std::cout << (*b) + 7;
std::cout << std::endl;
Correct answer:

(*b) + 7 is an expression. It is evaluated in parts:

  • (*b) means following the address in b and treating the result as the variable a.
  • (*b) + 7 -> a + 7; a is replaced with the value in a because it is used as an expression.
  • 5 + 7 -> 12.
uint8_t a{ 5 };
int* b{ &a };
Correct answer:

Compilation error (see the video about pointers)

int a = 5;
uint8_t* b = &a;
Correct answer:

Compilation error (see the video about pointers)

Will b and c contain the same address?

int a = 5;
int* b = &a;
a = 6;
int* c = &a;
Correct answer:

They will contain the same address.

Variables never change their address. a = 6 writes 6 into the existing memory cell. It does not redirect a to another cell.

&a takes the address of cell a, not the value in it. It will always give the same address, regardless of which value is stored in a.

int a = 5;
int* ap = &a;
*ap = 6;
std::cout << a;
std::cout << std::endl;
a = 7;
std::cout << *ap;
std::cout << std::endl;
Answer:

Both reads and writes occur at the same address. ap contains the address of variable a. Writing to or reading from *ap is equivalent to working with a directly.

Is something like this allowed?

int a;
int* b = &a;
*b = 5;
std::cout << a;
std::cout << std::endl;
Answer:

A value can be assigned to an uninitialized variable through a pointer. This is allowed.

int a = 5;
int* p = &a;
*p = 6;
int b = 7;
p = &b;
*p = 8;
std::cout << a;
std::cout << std::endl;
std::cout << b;
std::cout << std::endl;
Answer:

On the line p = &b, the address in p itself is overwritten with the address of another variable (b).

*p = 8 now writes 8 into b.

int a = 5;
int b = 6;
int* p = &a;
int** pp = &p;
**pp = 7;
*pp = &b;
**pp = 8;
std::cout << a;
std::cout << std::endl;
std::cout << b;
std::cout << std::endl;
Answer
int a = 5; // скажем, адрес = 32
int b = 6; // скажем, адрес = 36
int* p = &a; // адрес p = 40, адрес в p = 32
int** pp = &p; // адрес в pp = 40
**pp = 7; // *(*pp) --> *(40) --> *(p) --> *32 --> a
// то есть a = 7
*pp = &b; // адрес в p = 36
**pp = 8; // *(*pp) --> *(40) --> *(p) --> *36 --> b
// то есть b = 8
std::cout << a;
std::cout << std::endl;
std::cout << b;
std::cout << std::endl;
int a = 7;
int* pa = &a;
void* voidp = pa;
uint8_t c = 9;
uint8_t* pc = &c;
std::cout << sizeof(pa);
std::cout << std::endl;
std::cout << sizeof(voidp);
std::cout << std::endl;
std::cout << sizeof(pc);
std::cout << std::endl;
Answer

Pointers of any type have the same size because they only store memory addresses.

On 64-bit systems, any pointer is 64 bits in size (you are most likely on a 64-bit system).

int a = 7;
int ap = &a;
std::cout << sizeof(a);
std::cout << std::endl;
std::cout << sizeof(ap);
std::cout << std::endl;
std::cout << sizeof(*ap);
std::cout << std::endl;
Answer

sizeof(a) is the same as sizeof(int), 4.

sizeof(ap) is the same as sizeof(int*), 8.

sizeof(*ap) does not evaluate the expression *ap; it only determines the type of its result. The expression *ap has type int, so sizeof(int), which is 4, is calculated.

void* p = nullptr;
p = static_cast<void*>(&p);
std::cout << p;
std::cout << std::endl;
std::cout << &p;
std::cout << std::endl;
Answer

&p takes the address of the variable p itself. The type of the expression &p is void** (the address of a variable of type void*).

The static_cast<void*>(...) explicitly converts this void** to void*, that is, to the type of the variable p itself. So the assignment p = static_cast<void*>(&p) stores the address of p itself in p — now p points to itself.

Two identical addresses are printed: the value stored in p matches the address &p.

If the static_cast<void*> is omitted, the conversion from void** to void* still happens implicitly.

17. Copying a variable and a pointer with similar names

Section titled “17. Copying a variable and a pointer with similar names”
int x{ 0 };
int* px{ &x };
int y{ x };
int* py{ px };
*px = 1;
std::cout << x << std::endl;
std::cout << y << std::endl;
std::cout << *px << std::endl;
std::cout << *py << std::endl;
Answer:

int y{ x } copies the value from x — y is independent of x afterwards, so writing through the pointer does not affect it.

int* py{ px } copies the address from px — both pointers point to the same variable x.

*px = 1 writes 1 into x, so the output is:

  • x — 1,
  • y — 0 (copied before the assignment),
  • *px — 1,
  • *py — 1 (same address as px).