An Overture to C Programmers
xli
Under the hood, references are equivalent to pointers because they’re
also a zero-overhead abstraction. The compiler produces similar code. To
illustrate this, consider the results of compiling the
make_sentient
functions
on GCC 8.3 targeting x86-64 with
-O2
. Listing 6 contains the assembly gen-
erated by compiling Listing 5.
make_sentient(HolmesIV*):
mov BYTE PTR [rdi], 1
ret
make_sentient(HolmesIV&):
mov BYTE PTR [rdi], 1
ret
Listing 6: The assembly generated from compiling Listing 5
However, at compile time, references provide some safety over raw
pointers because,
generally speaking, they cannot be null.
With pointers, you might add a
nullptr
check to be safe. For example,
you might add a check to
make_sentient
, as in Listing 7.
void make_sentient(HolmesIV* mike) {
if(mike == nullptr) return;
mike->is_sentient = true;
}
Listing 7: A refactor of
make_sentient
from Listing 5 so it performs a
nullptr
check
Such a check is unnecessary
when taking a reference; however, this
doesn’t mean that references are always valid. Consider the following function:
HolmesIV& not_dinkum() {
HolmesIV mike;
return mike;
}
The
not_dinkum
function returns a reference, which is guaranteed to be
non-null. But it’s pointing to garbage memory (probably in the returned-
from stack frame of
not_dinkum
). You must never do this. The result will
be utter misery, also known as
undefined runtime behavior: it
might crash, it
might give you an error, or it might do something completely unexpected.
One other safety feature of references is that they can’t be
reseated. In
other words, once a reference is initialized, it can’t be changed to point to
another memory address, as Listing 8 shows.
int main() {
int a = 42;
int& a_ref = a;
u
int b = 100;
a_ref = b;
v
}
Listing 8: A program illustrating that references cannot be reseated
xlii
An Overture to C Programmers
You
declare
a_ref
as a reference to
int a
u
. There is no way to reseat
a_ref
to point to another
int
. You might try to reseat
a
with
operator=
v
, but
this actually sets the value of
a
to the value of
b
instead
of setting
a_ref
to
reference
b.
After the snippet is run both
a
and
b
are equal to
100
, and
a_ref
still points to
a
. Listing 9 contains equivalent code using pointers instead.
int main() {
int a = 42;
int* a_ptr = &a;
u
int b = 100;
*a_ptr = b;
v
}
Listing 9: An equivalent program to Listing 8 using pointers
Here, you declare the pointer with a
*
instead of a
&
u
. You assign the
value of
b
to
the memory pointed to by
a_ptr
v
. With references, you don’t
need any decoration on the left side of the equal sign. But if you omit the
*
in
*a_ptr
, the compiler would complain that you’re trying to assign an
int
to
a pointer type.
References are just pointers with extra safety precautions and a sprinkle
of syntactic sugar. When you put a reference on the left side of an equal sign,
you’re setting the pointed-to value equal to the right side of the equal sign.
Dostları ilə paylaş: