Here is a memory map; a representation of memory as a sequence of blocks:
address 01 02 03
+----+----+----+...
data within | 23 | 6f | 4a |
+----+----+----+...
Now suppose we create a character:
char c = 'z'; // 'z' is 7a in hex
Further suppose c
is stored at address 01
, so our memory looks like so:
address 01 02 03
+----+----+----+...
data within | 7a | 6f | 4a |
+----+----+----+...
Now, let's create a pointer:
char* p = &c; // point at c
p
may be stored at address 02
:
address 01 02 03
+----+----+----+...
data within | 7a | 01 | 4a |
+----+----+----+...
Here the pointer p
is at address 02
and it points at address 01
. That's the meaning of p = &c;
. When we dereference the pointer p
(at address 02
) we look at what's in the address pointed at by p
. That is, p
points at address 01
, and so dereferencing p
means looking inside address 01
.
Finally, lets create a reference:
char& r = c;
Here the memory layout doesn't change. That is, no memory is used to store r
. r
is a sort of alias for c
, so when we refer to r
we practically refer to c
. r
and c
are conceptually one. Changing r
means changing c
, and changing c
means changing r
.
When you create a reference you must initialize, and once initialized you cannot re-initialize it with another target. That is, above the reference r
means and forever will mean c
.
Also related are const references. These are the same as a reference, except they are immutable:
const char& r = c;
r = 'y'; // error; you may not change c through r
c = 'y' // ok. and now r == 'y' as well
We use const references when we are interested in reading the data but frown upon changing it. By using a const reference the compiler will not copy the data, so this gives us ideal performance, but also forbid us from changing the data, for correctness.
In a sense, you can say that references are a compile-time feature, whereas pointers are a runtime feature. So references are faster and cheaper than pointers, but come with certain constraints and implications. Like other compile-time-vs-runtime alternatives, we sometimes pick one over the other for performance, sometimes for static analysis and sometimes for flexibility.