First thing's first: A pointer is simply an
address in memory. That's all it is, by itself. It's a number.
Every time you declare a variable, that variable will have an address, and this address is where that variable is stored in memory. The compiler/runtime decides for you what this address will be.
1 2 3 4 5 6
|
int main()
{
int a = 42; // declare a variable
int* ptr; // declare a pointer-to-int. Note: we have not assigned it to anything
ptr = &a; // the & operator here takes the address of a.
}
|
In the above example, we set
ptr to be the
address of a. In other words, we set ptr to
point to a.
To repeat myself: ptr by itself is just the memory address of a.
But when you
dereference ptr, you get the actual data at the address (pointer) you dereference.
You dereference a pointer using the * unary operator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
// Example program
#include <iostream>
#include <string>
int main()
{
int a = 42;
int b = 7;
int* ptr = &a;
// The follow is dereferencing a pointer.
// when you DEreference a pointer, you get a REFERENCE to the object or data it points to.
int data = *ptr; // dereference the pointer; copy the data into data
std::cout << data << std::endl;
// This is re-assigning the pointer to point to another location in memory (another variable)
ptr = &b;
std::cout << *ptr << std::endl;
}
|
____________________________________
The above are rather simple, contrived examples to just demonstrate the syntax of pointers.
In modern C++, you should prefer to use references when passing objects instead of pointers, unless using a pointer is necessary.
To actually know if we want the ptr itself, or *ptr (the data it points to), we have to put it in context. The issue here is that any real example that requires pointers has to be complicated enough to warrant the use of pointers. Linked Lists and Trees are classic examples that use pointers.
But for now, let's go a bit simpler and just swap two variables. In modern C++, you should be pass-by-reference for this, but let's pretend we have to use pointers.
Scenario A:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
// Example program
#include <iostream>
void swap(int t, int u)
{
int temp = t;
t = u;
u = temp;
}
int main()
{
int a = 5;
int b = 3;
std::cout << a << " " << b << std::endl;
swap(a, b);
std::cout << a << " " << b << std::endl;
}
|
Run this code. You'll see the output is:
Why didn't it swap? This is because when C++ passes an object into a function, it by default does copy-by-value. Only the local copies were swapped, but not the actual data in main.
To actually swap the numbers, we need to work with the actual data in main, and not a copy of them. This is done by passing the addresses of the variables in main, instead of copying the values themselves.
Scenario B:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
// Example program
#include <iostream>
void swap(int* ptr_a, int* ptr_b)
{
int temp = *ptr_a;
*ptr_a = *ptr_b;
*ptr_b = temp;
}
int main()
{
int a = 5;
int b = 3;
std::cout << a << " " << b << std::endl;
swap(&a, &b);
std::cout << a << " " << b << std::endl;
}
|
When we call swap(&a, &b) we are passing the addresses of a and b.
The function sees these as pointers. Now, when we dereference ptr_a, using *ptr_a, we are modifying the actual data that the pointer (address) points to.
Long winded and rambly... but hope you get the idea a bit better now.
___________________________________
Also, there really isn't ever a reason to actually print the pointer itself (and print out the address). Might be good for debugging purposes, but the address used is something you can't control.
You can, however, set a pointer to be a "null pointer", which signifies that it's not point to any data. Dereferencing a null pointer causes undefined behavior.
int* ptr = nullptr;