*
and &
are one of the first hurdles that programmers new to C and C++ have to take.
To really understand these concepts, it helps to know a bit more about how memory works in these languages.
First of all: C++ is just C but with classes and many other additional features. Almost all C programs are valid C++ programs. C++ even started out as a language that was compiled to C first.
Memory is, roughly speaking, divided in two parts, a 'stack' and a 'heap'. There are also other places for the code itself and compile-time constants (and maybe a few more) et cetera but that doesn't matter for now. Variables declared within a function always live on the stack. Let's see this in action with a simple example and analyse how memory is organized to build a mental model.
#include <iostream>
void MyFunction() {
int intOnStack = 5;
int* intPtrOnStack = new int(6); // This int pointer points to an int on the heap
std::cout << intOnStack << *intPtrOnStack;
delete intPtrOnStack;
}
int main() { MyFunction(); }
This program prints 56
when executed. So what happens when MyFunction()
gets called? First, a part of the stack is reserved for this function to work with. When the variable intOnStack
is declared within the function, it is placed in this part of the stack and it is initialized with (filled with) the int
value 5
.
Next, the variable intPtrOnStack
is declared. intPtrOnStack
is of type int*
. int*
's point to int
's by containing their memory-address. So an int*
is placed on the stack and it is initialized with the value that results from the expression new int(6)
. This expression creates a new int on the heap and returns the memory-address of this int
(an int*
) to it. So that means that intPtrOnStack
now points to the int
on the heap. Though the pointer itself lives on the stack.
The heap is a part of memory that is 'shared' by all functions and objects within the program. The stack isn't. Every function has its own part of the stack and when the function ends, its part of the stack is deallocated.
So int*
's are just memory-addresses of int
's. It doesn't matter where the int
lives. int*
's can also point to int
's on the stack:
#include <iostream>
void MyFunction() {
int intOnStack = 5;
int* intPtrOnStack = &intOnStack; // This int pointer points to intOnStack
std::cout << intOnStack << *intPtrOnStack;
}
int main() { MyFunction(); }
This prints 55
. In this example we also see the &
-operator in action (there are several uses of &
like the bit-wise-and, I'm not going into them).
&
simply returns the memory-address (a pointer!) of its operand. In this case its operand is intOnStack
so it returns its memory-address and assigns it to intPtrOnStack
.
So far, we've seen only int*
as types of pointers but there exist pointer-types for each type of object that has a memory-address, including pointers. That means that a thing like int**
exists and simply means 'pointer to a pointer to an int
'. How would you get one? Like this: &intPtrOnStack
.
Can pointers only live on the stack? No: new int*(&intPtrOnStack)
. Or new int*(new int(5))
.