This article is about a general notion of reference in computing. For the more specific notion of reference
used in C++, see [[Reference (C++)]].
In computer science, a reference is an object containing information which refers to data stored elsewhere, as opposed to containing
the data itself. Accessing the value referred to by a reference is called
dereferencing it. References are fundamental to constructing many data structures
(such as linked lists) and in exchanging information between different parts of a
program.
Address analogy
A reference may be compared to the address of a house. It is a small identifier from which it is possible to find a
potentially much larger object. Finding a house based on its address is analogous to dereferencing a reference.
In a more complicated example, suppose you leave a forwarding address in your old house each time you move. A person could
visit your first house, then follow the forwarding address to the next house, and so on until they finally find your current
house. This is analogous to how references are used in singly linked lists.
Another benefit of house addresses is that they're much easier to deal with than actual houses. Say you want to be able to
easily locate people on your street based on their last name. One way to do this is to use a large crane to physically pick up
and rearrange all the houses based on the last names of the residents. A much easier solution is to make a list of addresses of
people on your street and sort it by their last names. References have the same benefit: it is possible to manipulate references
to data without actually having to modify the data itself, which in some cases can be much more efficient.
Benefits
References increase flexibility in where objects can be stored, how they are allocated, and how they are passed between areas
of code. As long as we can access a reference to the data, we can access the data through it, and the data itself need not be
moved. They also make sharing of data between different code areas easier; each keeps a reference to it.
The mechanism of references, if varying in implementation, is a fundamental programming language feature common to nearly all
modern programming languages. Even some languages that support no direct use of references have some internal or implicit use.
For example, the call by reference calling convention can be implemented with either
explicit or implicit use of references.
Pointers are the most primitive and error-prone but also one of the most powerful
and efficient types of references, storing only the address of an object in memory. Smart
pointers are opaque data structures that act like pointers but can only be
accessed through particular methods.
A file handle, or handle is a type of reference used to abstract file content. It usually represents both the
file itself, as when requesting a lock on the file, and a specific position
within the file's content, as when reading a file.
Formal representation
More generally, a reference can be considered as a piece of data that allows unique retrieval of another piece of data. This
includes primary keys in databases and keys in an
associative array. If we have a set of data D, any well-defined (single-valued)
function from D onto D ∪ {null} defines a type of reference, where
null is the image of a piece of data not referring to anything meaningful.
An alternative representation of such a function is a directed graph called a reachability
graph. Here, each datum is represented by a vertex and there is an edge from u to v if the datum in u
refers to the datum in v. The maximum out-degree is one. These graphs are
valuable in garbage collection, where they can be used to separate
accessible from inaccessible objects.
External and internal storage
In many data structures, large, complex objects are composed of smaller objects. These objects are typically stored in one of
two ways:
- With internal storage, the contents of the smaller object are stored inside the larger object.
- With external storage, the smaller objects are allocated in their own location, and the larger object only stores references
to them.
Internal storage is usually more efficient, because there is a space cost for the references and dynamic allocation metadata, and a time cost associated with dereferencing a reference and
with allocating the memory for the smaller objects. Internal storage also enhances locality of reference by keeping different parts of the same large object close together in
memory. However, there are a variety of situations in which external storage is preferred:
- If the data structure is recursive, meaning it may contain itself. This cannot be represented in the internal way.
- If the larger object is being stored in an area with limited space, such as the stack, then we can prevent running out of
storage by storing large component objects in another memory region and referring to them using references.
- If the smaller objects may vary in size, it's often inconvenient or expensive to resize the larger object so that it can
still contain them.
- References are often easier to work with and adapt better to new requirements.
Some languages, such as Java and Scheme, do not support internal storage. In these languages, all objects are uniformly
accessed through references.
Language support
In assembly languages, the first languages used, it is typical to express
references using either raw memory addresses or indexes into tables. These work, but are somewhat tricky to use, because an
address tells you nothing about the value it points to, not even how large it is or how to interpret it; such information is
encoded in the program logic. The result is that misinterpretations can occur in incorrect programs, causing bewildering
errors.
One of the earliest opaque references was that of the Lisp programming
language cons cell, which is simply a record
containing two references to other Lisp objects, including possibly other cons cells. This simple structure is most commonly used
to build singly linked lists, but can also be used to build simple binary trees and so-called "dotted lists", which terminate not with a null reference but a value.
Another early language, Fortran, does not have an explicit representation of references, but does use them implicitly in its
call-by-reference calling semantics.
The pointer is still one of the most popular types of references today. It is
similar to the assembly representation of a raw address, except that it carries a static datatype which can be used at compile-time to ensure that the data it refers to is not misinterpreted.
However, because C has a weak type system which can be violated using casts (explicit conversions between various pointer types and between pointer types and integers),
misinterpretation is still possible, if more difficult. Its successor [[C++]] tried to increase type safety of pointers with new cast operators and smart pointers in its standard library, but still retained the ability to circumvent these safety mechanisms for compatibility.
A number of popular mainstream languages today such as Java,
C#, and Visual Basic have adopted a much more opaque type
of reference, usually referred to as simply a reference. These references have types like C pointers indicating how to
interpret the data they reference, but they are typesafe in that they cannot be interpreted as a raw address and unsafe
conversions are not permitted. In those managed languages, the references are actually pointers of pointers of the
referred data. In C/C++, the reference concept of managed languages means two-step pointing. The Garbage Collector is the sole actor that can directly access the mid-step
pointers, which cause the opacity. Typically pointer arithmetic is also not supported.
Fortran
A Fortran reference is best thought of as an alias of another object, such as a scalar
variable or a row or column of an array. There is no syntax to dereference the reference or manipulate the contents of the
referent directly. Fortran references can be null. As in other languages, these references facilitate the processing of dynamic
structures, such as linked lists, queues, and trees.
Functional languages
In all of the above settings, the concept of mutable variables, data that can be modified,
often makes implicit use of references. In Standard ML, O'Caml, and many other functional languages, most values are persistent: they cannot be modified by
assignment. Assignable "reference cells" serve the unavoidable purposes of mutable references in imperative languages, and make
the capability to be modified explicit. Such reference cells can hold any value, and so are given the polymorphic type α ref, where α is to be replaced with the type of value
pointed to. These mutable references can be pointed to different objects over their lifetime. For example, this permits building
of circular data structures.
To preserve safety and efficient implementations, references cannot be type-cast in ML, nor can pointer arithmetic be
performed. It is important to note that in the functional paradigm, many structures that would be represented using pointers in a
language like C are represented using other facilities, such as the powerful algebraic
datatype mechanism. The programmer is then able to enjoy certain properties (such as the guarantee of immutability) while
programming, even though the compiler often uses machine pointers "under the hood".
Equality
In languages where all objects are accessed through references (like Java
and Scheme), there becomes a need to test for two different types of
equality:
- whether two references reference the same object. In Java, this is done using the equality operator (==). In Scheme, this is
done using the procedure
eq?.
- whether the objects referenced by two references are equal in some sense (e.g. their contents are the same). In Java, this is
done using the
.equals() method, which all reference types possess as it is inherited from Object. In
Scheme, this is done with the procedure equal?.
The first type of equality implies the second, but the converse is not necessarily true. For example, two objects may be
distinct (in the first sense) but contain the same string (in the second sense). See
identity for more of this issue.
See also
External links
- Pointer Fun With Binky Introduction to
pointers in a 3 minute educational video - Stanford Computer Science Education Library
This entry is from Wikipedia, the leading user-contributed encyclopedia. It may not have been reviewed by professional editors (see full disclaimer)