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
|| BCPL used
logor. This was because most keyboards din't have
∨ 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
-> was taken for syntactic sugar around field derefrences
struct_pointer->field which is
Other options like
<- could create ambiguous parsings. For example:
foo <- bar
Is that to be read as:
(foo) <- (bar)
(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,
& 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."
& 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.