Pointers
Concepts
Section titled “Concepts”- 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
Exercises for understanding
Section titled “Exercises for understanding”Explain what will happen in the following code snippets. Also run the snippets to verify that your reasoning is correct.
When
mainis not shown, place the code in a typicalmainfile:#include <iostream>#include <cstdint>int main(){// сюда}
1. Variable address
Section titled “1. Variable address”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:
- a type;
- a variable name;
- optionally, an initialization.
int* b{ &a }; — here:
int*is the type;bis the variable name;{ &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.
2. Address of an uninitialized variable
Section titled “2. Address of an uninitialized variable”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.
3. Dereference operator (writing)
Section titled “3. Dereference operator (writing)”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.
4. A number as an address
Section titled “4. A number as an address”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).
5. Dereference operator (reading)
Section titled “5. Dereference operator (reading)”int a{ 5 };int b{ *(&a) };std::cout << b;std::cout << std::endl;Correct answer:
Evaluating the expression *(&a):
&abecomes the address of variablea(say, 32).*32follows the address, allowing access to variablea.- When
ais used as an expression, it yields the value 5.
*(&a) -> *32 -> a -> 5
6. Printing a complex expression
Section titled “6. Printing a complex expression”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 inband treating the result as the variablea.(*b) + 7->a + 7;ais replaced with the value inabecause it is used as an expression.5 + 7->12.
7. Pointer to a larger data type
Section titled “7. Pointer to a larger data type”uint8_t a{ 5 };int* b{ &a };Correct answer:
Compilation error (see the video about pointers)
8. Pointer to a smaller data type
Section titled “8. Pointer to a smaller data type”int a = 5;uint8_t* b = &a;Correct answer:
Compilation error (see the video about pointers)
9. Dependence of the address on the value
Section titled “9. Dependence of the address on the value”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.
10. It is the same memory!
Section titled “10. It is the same memory!”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.
11. Assigning a value through a pointer
Section titled “11. Assigning a value through a pointer”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.
12. Reassigning a pointer
Section titled “12. Reassigning a pointer”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.
13. Double pointer
Section titled “13. Double pointer”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; // скажем, адрес = 32int b = 6; // скажем, адрес = 36int* p = &a; // адрес p = 40, адрес в p = 32int** 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;14. Pointer sizes
Section titled “14. Pointer sizes”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).
15. Pointer and variable sizes
Section titled “15. Pointer and variable sizes”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.
16. Pointer to itself
Section titled “16. Pointer to itself”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 aspx).