References destroy readability and more
C++ features a feature called references, these are implicit
pointers — pointers without the pointer syntax. A reference
is a guarantee that a pointer is not null, it disables swapping
out the pointer inside the code (basically, it's a pointer,
not to a const-object, but a pointer, that is
const), and only the one object can be accessed
(well, not really, but it's absolutely clear that their's
only one object, and accessing the next one is awkward).
This is all well and good, but you should need not a reference
to declare a pointer as non-null, instead at the very least it
should documented, but preferable the language syntax shall
declare whether or not the pointer is null, e.g. by appending
a question mark to declare that the pointer may be null
(which should be strict from the address used for null
(usually 0, but that is not guaranteed), so nullptr
is actually a good idea, even though that is not the reason
it exists). Declaring that a pointer may or may not be null,
is also beneficial when it comes to multiobject pointers.
The real problem with references is that they are implicit
pointers, all other features it provides is just vitamin
enrichment on the vegetable. Implicitly is usually a bad
thing, and such is the case also when it comes to pointers.
When you have an implicit pointer, it is not obvious when
you read the code that you have pointer, you need to keep
track of whether an object is a reference or if you have
a local copy, are you output to the caller or not (it is
common practice to have a reference to a RAII-initialised
variable: an output reference, instead of an output pointer.
Worse, references and local copies are usually used when
the other should be used.
Unlike C++, in C, you avoid aggregate parameters and
returns: meaning you you always use a pointer when you
a struct, union or array (array
parameters are always pointers). This has the advantage
that you know that you always have a pointer to the
callers object, and if you need to make non-propagating
changes, you have to explicitly copy it. This makes it
obvious whether or not you intend to modify the callers
copy or just your own copy, and if you don't need a copy,
you don't get a copy, saving CPU cycles and memory
(expect in the very unlikely case that the structure is
smaller than an address and the argumment must be
passed through the stack). Having a single way declare
that you are going to output an aggregate type, or
want one as input, stabilises both the API (breaks
when switch to or from pointers) and ABI (breaks when
switching to or from copies).