Here is the simple program we talked about in the previous lesson. We
only declare this variable and initialize it to 2.
I will print out the value stored inside of this variable and also the memory
address. I'm using the address of operator to get the address.
Alright, now let's compile this program.
The stored value is not a big surprise,
but let's take a look at the output for the memory address.
It is prepended with 0 and x,
which means that this is a hexadecimal number. In a decimal
notation, that number would look like this.
Either way,
we don't really care for the exact number since we are
never going to type up the address by hand.
That is what the reference operator is here for. Now let's say that I
want to store this memory address for later use.
I want to store it inside of this addr_of_x variable, but I don't know
which data type should I use to store memory addresses.
So let's use the auto keyword, which will force the
compiler to deduce the data type itself.
I will now hover over the identifier to see which data type was
chosen to store the address by the compiler. And it seems like the
data type is integer, but there is also this asterisk character
next to the variable name.
It is the same character we use as a multiplication operator, but
obviously in this case nothing is getting multiplied. C++ knows that
this is not a multiplication operator because of the context. In this
context, the asterisk is actually informing the compiler that we want to
use this variable as a pointer.
So what is a pointer?
Pointer is just a variable that holds a memory address.
In other words, if you want to store a memory address inside of a variable,
you can store it inside of a pointer. And pointer is just a
variable with this character in front of it.
This is what separates it from other data types.
So if this character tells the compiler that this variable is a pointer,
why do we also need this int keyword?
We will discuss this in more detail later, but for now, just
remember that if your pointer variable is holding an address
at which the integer is stored, then the pointer should also be an integer.
So, as an example, if this variable was a double,
we would also need to put double here so that we can store an address to that
place in memory which holds that type of data. Before we move on, let's try to
imagine how this looks like behind the scenes.
So, again,
here is our variable x. Compiler will assign an address to this
variable and store the number 2 inside of it. And now we can create
another variable which will store that address.
This variable also gets assigned some other place in memory. I will
keep this asterisk character as a part of the identifier so that we
always know which variable is a pointer, but in practice pointers are
just like any other variable.
They also have their own place in memory,
their own memory address, and they can also store some value.
In this case, we are taking the address of the variable x
and storing it inside of the pointer.
And since this address is holding an integer,
we need to also define this pointer variable as an integer.
So why are these address storing variables called pointers?
Well, because we can kind of imagine that they point to another place in memory.
In this case, the addr_of_x pointer points to the variable x.
Let's print out the addr_of_x variable in the same way
and see if we get what we expected.
Now, let's compile the program again.
And did we get what we expected?
Well,
it seems like we did. The most important part of this output is that
the memory address of x and the value stored in the pointer variable
are the exact same memory address.
This last line was just to show you that pointers are just like other variables.
They also have their own place in memory.
So you can basically have pointers to pointers, variables
that store a memory address of another pointer that stores
another memory address, and so on.
You might notice that your addresses are different than mine, and that is okay.
As I mentioned, different OSs and architectures store
the addresses in different ways.
But you can also notice that every time we recompile the
program we get different memory addresses.
This is done on purpose, mostly to increase security of your
programs, and if you want to know more about this,
you can read about address space layout randomization, or ASLR. I just
wanted you to know that this is the expected behavior.
As you can see, pointers are a really simple concept,
but if you never used them before,
you might want to take it slow and try to draw
memory like I did for these examples,
at least in the beginning. I always put this character next to the
variable name, and I will continue to do so throughout the course,
but you may also see programmers declare a pointer with the asterisk
next to the data type like this.
It really doesn't matter, and it all depends on how you think about pointers,
but I urge you to declare pointers like me for one simple reason. Let's say
that you want to declare more than one pointer at the same time, so you may
type out a statement that looks something like this. But this will not create
three pointers to integers.
The first variable will be a pointer, but the other two will
be just simple integer variables, because the compiler will
see our statement like this.
The asterisk is only referring to the first variable.
However, if you add an asterisk to every variable,
this will create three pointers, just like we wanted, so I
advise you to declare them like this,
but the final decision is up to you. Everyone has their own coding style,
but when you choose your own, please be consistent, at least in
the same code base, because if you don't, well, that's just
rude. If you need to declare a pointer, but you don't want to
initialize it right away, set it to nullptr.
This is a special C++ keyword that defines a pointer
that doesn't point to anything.
It is basically the same as writing NULL or 0,
but using this keyword instead is recommended in
modern C++. If you don't do this, your pointer will hold some garbage data,
the collection of bits that was already there at the address in memory before
it was assigned to your pointer, and you don't want that because in some
situations this could be dangerous and lead to bugs.