Why does C use the asterisk for pointers?
Simply - because B did.
Because memory is a linear array, it is possible to interpret the value in a cell as an index in this array, and BCPL supplies an operator for this purpose. In the original language it was spelled rv
, and later !
, while B uses the unary *
. Thus, if p
is a cell containing the index of (or address of), or pointer to) another cell, *p
refers to the contents of the pointed-to cell, either as a value in an expression or as the target of an assignment.
From The Development of the C Language
Thats it. At this point, the question is as uninteresting as "why does python 3 use .
to call a method? Why not ->
?" Well... because Python 2 uses .
to call a method.
Rarely does a language exist from nothing. It has influences and is based on something that came before.
So, why didn't B use !
for derefrencing a pointer like its predecessor BCPL did?
Well, BCPL was a bit wordy. Instead of &&
or ||
BCPL used logand
and logor
. This was because most keyboards din't have ∧
or ∨
keys and not equal was actually the word NEQV
(see The BCPL Reference Manual).
B appears to have been partially inspired to tighten up the syntax rather than have long words for all these logical operators that programmers did fairly frequently. And thus !
for dereference became *
so that !
could be used for logical negation. Note there's a difference between the unary *
operator and the binary *
operator (multiplication).
Well, what about other options, like ->
?
The ->
was taken for syntactic sugar around field derefrences struct_pointer->field
which is (*struct_pointer).field
Other options like <-
could create ambiguous parsings. For example:
foo <- bar
Is that to be read as:
(foo) <- (bar)
or
(foo) < (-bar)
Making a unary operator that is composed of a binary operator and another unary operator is quite likely to have problems as the second unary operator may be a prefix for another expression.
Furthermore, it is again important to try to keep the things being typed frequently to a minimum. I would hate to have to write:
int main(int argc, char->-> argv, char->-> envp)
This also becomes difficult to read.
Other characters might have been possible (the @
wasn't used until Objective C appropriated it). Though again, this goes to the core of 'C uses *
because B did'. Why didn't B use @
? Well, B didn't use all the characters. There was no bpp
program (compare cpp) and other characters were available in B (such as #
which was later used by cpp).
If I may hazard a guess as to why - its because of where the keys are. From a manual on B:
To facilitate manipulation of addresses when it seems advisable, B provides two unary address operators, *
and &
. &
is the address operator so &x
is the address of x
, assuming it has one. *
is the indirection operator; *x
means "use the content of x as an address."
Note that &
is shift-7 and *
is shift-8. Their proximity to each other may have been a hint to the programmer as to what they do... but that's only a guess. One would have to ask Ken Thompson about why that choice was made.
So, there you have it. C is that way because B was. B is that way because it wanted to change from how BCPL was.
->
is being used in the C language as a dereference operator – when accessing fields in a struct:struct_pointer->field
, which is short for(*struct_pointer).field
.structs
for dereferencing, which seemed odd to me. It's a pointer symbol, right? Why not (<-
) for dereferencing? Am i really the only one that thinks this way?^
is used and can be thought of a rotated arrow and read as "point to", same meaning as->
but shorter.^integer
means "pointer to integer" for type declaration, andvar^
means "the memoryvar
points to" for dereferencing. The symbol position is more logical than C when reading from left to right, which always put after type and before variable name. Pascal also uses@
for taking address, which is better than&
, because@var
is "the address at which var is located"