Difference between f and &f [duplicate] - c++

It's interesting that using the function name as a function pointer is equivalent to applying the address-of operator to the function name!
Here's the example.
typedef bool (*FunType)(int);
bool f(int);
int main() {
FunType a = f;
FunType b = &a; // Sure, here's an error.
FunType c = &f; // This is not an error, though.
// It's equivalent to the statement without "&".
// So we have c equals a.
return 0;
}
Using the name is something we already know in array. But you can't write something like
int a[2];
int * b = &a; // Error!
It seems not consistent with other parts of the language. What's the rationale of this design?
This question explains the semantics of such behavior and why it works. But I'm interested in why the language was designed this way.
What's more interesting is the function type can be implicitly converted to pointer to itself when using as a parameter, but will not be converted to a pointer to itself when using as a return type!
Example:
typedef bool FunctionType(int);
void g(FunctionType); // Implicitly converted to void g(FunctionType *).
FunctionType h(); // Error!
FunctionType * j(); // Return a function pointer to a function
// that has the type of bool(int).

Since you specifically ask for the rationale of this behavior, here's the closest thing I can find (from the ANSI C90 Rationale document - http://www.lysator.liu.se/c/rat/c3.html#3-3-2-2):
3.3.2.2 Function calls
Pointers to functions may be used either as (*pf)() or as pf().
The latter construct, not sanctioned in the Base Document, appears in
some present versions of C, is unambiguous, invalidates no old code,
and can be an important shorthand. The shorthand is useful for
packages that present only one external name, which designates a
structure full of pointers to object s and functions : member
functions can be called as graphics.open(file) instead of
(*graphics.open)(file). The treatment of function designators can
lead to some curious , but valid , syntactic forms . Given the
declarations :
int f ( ) , ( *pf ) ( ) ;
then all of the following expressions are valid function calls :
( &f)(); f(); (*f)(); (**f)(); (***f)();
pf(); (*pf)(); (**pf)(); (***pf)();
The first expression on each line was discussed in the previous
paragraph . The second is conventional usage . All subsequent
expressions take advantage of the implicit conversion of a function
designator to a pointer value , in nearly all expression contexts .
The Committee saw no real harm in allowing these forms ; outlawing
forms like (*f)(), while still permitting *a (for int a[]),
simply seemed more trouble than it was worth .
Basically, the equivalence between function designators and function pointers was added to make using function pointers a little more convenient.

It's a feature inherited from C.
In C, it's allowed primarily because there's not much else the name of a function, by itself, could mean. All you can do with an actual function is call it. If you're not calling it, the only thing you can do is take the address. Since there's no ambiguity, any time a function name isn't followed by a ( to signify a call to the function, the name evaluates to the address of the function.
That actually is somewhat similar to one other part of the language -- the name of an array evaluates to the address of the first element of the array except in some fairly limited circumstances (being used as the operand of & or sizeof).
Since C allowed it, C++ does as well, mostly because the same remains true: the only things you can do with a function are call it or take its address, so if the name isn't followed by a ( to signify a function call, then the name evaluates to the address with no ambiguity.

For arrays, there is no pointer decay when the address-of operator is used:
int a[2];
int * p1 = a; // No address-of operator, so type is int*
int (*p2)[2] = &a; // Address-of operator used, so type is int (*)[2]
This makes sense because arrays and pointers are different types, and it is possible for example to return references to arrays or pass references to arrays in functions.
However, with functions, what other type could be possible?
void foo(){}
&foo; // #1
foo; // #2
Let's imagine that only #2 gives the type void(*)(), what would the type of &foo be? There is no other possibility.

Related

what is the meaning of (*(int (*)())a)()?

I am the beginner of learning C++. Today, I saw a pointer function like that
(*(int (*)())a)()
I was very confused with what the meaning of this and how I can understand it easily.
Let's add a typedef, to help make heads or tails out of it:
typedef int (*int_func_ptr)();
(*(int_func_ptr)a)();
So a is being cast to a function pointer of a particular prototype, dereferenced (which is redundant), and then called.
int (*)() is a function pointer type that returns int and accepts no parameters.
I presume that a is a function pointer whose type "erases" the actual type (perhaps so that one can store a bunch of different function pointers in a vector) that we need to cast to this pointer type, so (int(*)())a) will perform that casting.
Afterwards we want to call the function, so the provided code dereferences the pointer * and then calls it with parenthesis ()
Example
I have a function foo that looks like this:
int foo()
{
std::cout << "foo\n";
return 1;
}
And then via reinterpret_cast I get a pointer to a function that instead returns void (for type-erasure reasons):
void(*fptr)() = reinterpret_cast<void(*)()>(&::foo); //§5.2.10/6
Later, I want to call that function, so I need to re-cast it back to its original type, and then call it:
(*(int (*)())fptr)(); // prints `foo`
Demo
De-referencing it is actually unecessary and the following is equivalent:
((int (*)())fptr)();
The explanation for why they're equivalent boils down to "The standard says that both a function type and a function pointer type can be callable"
If you're standard savvy, you can check out §5.2.2[expr.call] that states
A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list of initializer-clauses which constitute the arguments to the function. The postfix expression shall have function type or pointer to function type
StoryTeller and Andy have given correct answers. I'll give general rules, additionally.
StoryTeller makes a correct and useful typedef with typedef int (*int_func_ptr)();, which defines a function pointer type. Two things are to remember here.
The general language design for typedefs: it exactly mimics a declaration of an object of the given type! Simply prefixing a declaration with typedef makes the declared identifier a type alias instead of a variable. That is, if int i; declares an integer variable, typedefint i; declares i as a synonym for the type int. So declaring a function pointer variable would simply read int (*int_func_ptr)();. Prefixing this with the typedef, as StoryTeller did, makes it a type alias instead.
Casts of function pointers are notoriously confusing. One reason are the necessary parentheses:
Parentheses serve several unrelated purposes:
They group expressions to indicate subexpression precedence, as in (a+b) * c.
They delimit function arguments, both in declarations and in calls.
They delimit type names used in casts.
We have parentheses for all three purposes here!
The operator precedence is "unnatural" for function pointer declarations. This is so, of course, because they are natural for the much more frequent uses: Without parentheses, the declaration would be the more familiar looking int *int_func();, which declares a function proper which returns an int pointer. The reason is that the argument parentheses have higher priority than the dereferencing asterisk, so that in order to infer the type we mentally execute the call first, and not the dereferencing. And something that can be called is a function.1 The result of the call can be dereferenced, and that result is an int.
Compare that to the original int (*int_func_ptr)();: The additional parentheses force us to dereference first, so the identifier must be a pointer of some kind. The result of the dereferencing can be called, so it must be a function; the result of the call is an int.
Another reason why function pointer declarations or typedefs look unnatural is that the declared identifier tends to be at the center of the expression. The reason is that operators to the left and to the right of the identifier are applied (the dereferencing, the function call, and then there is finally the result type declaration all the way to the left).
The next rule is about constructing casts. The type names used in casts are constructed from corresponding variable declarations simply by omitting the variable name! This is obvious in the simple cases: since int i declares an int variable, (int) without the i is the corresponding cast.
If we apply that to the function pointer type, int (*int_func_ptr)() is transformed to the weird-looking (int (*)()) by omitting the variable name and putting the type name in parentheses as required for a cast. Note that the parentheses which force precedence of the asterisk are still there, even though there is nothing to dereference! Without them, (int *()) would be derived from int *int_func() and therefore denote a function which returns a pointer.2
It is perhaps surprising that there is exactly one place in a declaration where a variable name can syntactically be, so that even very complicated type expressions in casts are well-defined: It is this one place where a variable name fits which defines the cast type.
With these rules, let's re-examine the original expression:
(*(int (*)())a)()
On the outermost level we have two pairs of parentheses. The second pair is empty and thus must be a function call operator. That implies that the operand to the left has function type:
*(int (*)())a
The operand is an expression in parentheses for precedence. It has three parts: The asterisk, an expression in parentheses, and an identifier a. Since there is no operator between the parenthesized expression and the variable a, it must be a cast, actually the one we scrutinized above. * and the type cast have the same precedence and are evaluated right-to-left: first a is cast to a function pointer, and then * dereferences that in order to obtain the "function proper" (which are no real objects in C++). This fits because the function call operator from above will be applied to this result.
1 That C permits calling function pointers directly as well, without dereferencing first, is syntactic sugar and not considered in declarations.
2 While the expression is syntactically valid, such a cast to function is not allowed in C or C++.

Function-pointer syntax ambiguity

Take the following example. I create a function pointer named s, set it to f and call it. This compiles fine of course:
void f() {}
int main() {
void (*s)();
s = f;
s();
}
But take this next example, where I declare s now as a "function reference" (if it's so called) and set to f inline. This compiles fine as well:
void f() {}
int main() {
void (&s)() = f;
s();
}
What are the differences between these two ways to create and initialize a function-pointer? Note that when I use the reference syntax I am required to initialize it "in-line" to f whereas with the "pointer" syntax I had the ability to do it both ways. Can you explain that as well? And with that, can you explain what their differences are in terms of usability, and when must I use one form over the other?
Fundamentally the calling side has no distinct difference. But the decl side definitely does. As you have pointed out, references must be initialized to reference something. This makes them "safer", but even then there is no guarantee of "safety".
The function pointer need NOT point to a function at all. It could be NULL, or even uninitialized (pointing to garbage). Not that it matters, because you can always change it later (something you can NOT do with references).
void (*s)(); // uninitialized
or
void (*s)() = NULL; // set to null
and later
void foo()
{
}
s = foo;
You can do none of those with a reference. The reference must be initialized to something and preferabley something valid:
void (&f)() = foo; // ok. also references foo().
void (&f)() = *s; // also ok, but wait, is s valid?? does it have to be??
However, even here a function reference is not guaranteed to be safe, just safer. You can certainly do this:
void (*s)();
void (&f)() = *s;
You may get a compiler warning out of this (I do, "s used before being initialized") but in the end f still is now a reference to a "function" that isn't a function at all; just the random stack garbage in the s pointer. Worse, since references cannot be reassigned. this thing will always point at garbage.
The differences are the same as for any pointer/reference.
References must be initialized and cannot be reassigned later:
int i,j;
int &r = i;
r = j; // i = j, not &r == &j
References cannot be treated as objects distinct from the object they reference (as opposed to pointers, which are objects distinct from the object they point at)...
int i;
int *p = &i; // &p != &i
int &r = i; // &r == &i
Using a function pointer looks syntactically the same as using a reference, but that's because of a special rule with function pointers that allows you to use them without dereferencing them.
You said it yourself, a difference is that with a reference, you have to bind it upon declaration, which guarantees that references always refer to valid objects.
Another difference is that references cannot be rebinded after they are declared, so they refer to one and only one object throughout their lives.
Other than that they are the same thing.
I have met some purists that prefer references and said that pointers are a vestige of C that shouldn't be used.
Other people prefer pointers because they are more "explicit" about the fact that they are pointers.
Whether using one or the other depends on your needs. The way to choose is, use a reference if possible, but if you really need to be able to point it to a different function, then use a pointer.
A reference to a type P is a lot like a const pointer to a type P (not a pointer to a const P, which is different).
As it happens most of the ways they differ are not important if your type P is a function type. & behaves slightly differently, you can directly assign the pointer to a non const pointer, and functions that take one may not take the other.
If the type P was not a function type there would be loads of other differences -- operator=, lifetime of temporaries, etc.
In short, the answer is 'not much'.
Function identifiers are expressions of function type, but they implicitly convert to pointer-to-function type or reference-to-function type. So they can be passed to constructor of either reference or pointer and to operator= of pointer.
Since references syntactically behave like instances, there is no way to act on the reference rather than the referred object. That's why they can only be initialized. By the way prefered syntax in C++ is with parenthesis, not =.
You should use reference when possible and pointers only if you can't use reference. The reason is that since many things can't be done to reference (pointing to NULL, changing referred object, deleting it etc.) there is fewer things you have to look for when reading the code. Plus it saves some * and & characters.

Why is using the function name as a function pointer equivalent to applying the address-of operator to the function name?

It's interesting that using the function name as a function pointer is equivalent to applying the address-of operator to the function name!
Here's the example.
typedef bool (*FunType)(int);
bool f(int);
int main() {
FunType a = f;
FunType b = &a; // Sure, here's an error.
FunType c = &f; // This is not an error, though.
// It's equivalent to the statement without "&".
// So we have c equals a.
return 0;
}
Using the name is something we already know in array. But you can't write something like
int a[2];
int * b = &a; // Error!
It seems not consistent with other parts of the language. What's the rationale of this design?
This question explains the semantics of such behavior and why it works. But I'm interested in why the language was designed this way.
What's more interesting is the function type can be implicitly converted to pointer to itself when using as a parameter, but will not be converted to a pointer to itself when using as a return type!
Example:
typedef bool FunctionType(int);
void g(FunctionType); // Implicitly converted to void g(FunctionType *).
FunctionType h(); // Error!
FunctionType * j(); // Return a function pointer to a function
// that has the type of bool(int).
Since you specifically ask for the rationale of this behavior, here's the closest thing I can find (from the ANSI C90 Rationale document - http://www.lysator.liu.se/c/rat/c3.html#3-3-2-2):
3.3.2.2 Function calls
Pointers to functions may be used either as (*pf)() or as pf().
The latter construct, not sanctioned in the Base Document, appears in
some present versions of C, is unambiguous, invalidates no old code,
and can be an important shorthand. The shorthand is useful for
packages that present only one external name, which designates a
structure full of pointers to object s and functions : member
functions can be called as graphics.open(file) instead of
(*graphics.open)(file). The treatment of function designators can
lead to some curious , but valid , syntactic forms . Given the
declarations :
int f ( ) , ( *pf ) ( ) ;
then all of the following expressions are valid function calls :
( &f)(); f(); (*f)(); (**f)(); (***f)();
pf(); (*pf)(); (**pf)(); (***pf)();
The first expression on each line was discussed in the previous
paragraph . The second is conventional usage . All subsequent
expressions take advantage of the implicit conversion of a function
designator to a pointer value , in nearly all expression contexts .
The Committee saw no real harm in allowing these forms ; outlawing
forms like (*f)(), while still permitting *a (for int a[]),
simply seemed more trouble than it was worth .
Basically, the equivalence between function designators and function pointers was added to make using function pointers a little more convenient.
It's a feature inherited from C.
In C, it's allowed primarily because there's not much else the name of a function, by itself, could mean. All you can do with an actual function is call it. If you're not calling it, the only thing you can do is take the address. Since there's no ambiguity, any time a function name isn't followed by a ( to signify a call to the function, the name evaluates to the address of the function.
That actually is somewhat similar to one other part of the language -- the name of an array evaluates to the address of the first element of the array except in some fairly limited circumstances (being used as the operand of & or sizeof).
Since C allowed it, C++ does as well, mostly because the same remains true: the only things you can do with a function are call it or take its address, so if the name isn't followed by a ( to signify a function call, then the name evaluates to the address with no ambiguity.
For arrays, there is no pointer decay when the address-of operator is used:
int a[2];
int * p1 = a; // No address-of operator, so type is int*
int (*p2)[2] = &a; // Address-of operator used, so type is int (*)[2]
This makes sense because arrays and pointers are different types, and it is possible for example to return references to arrays or pass references to arrays in functions.
However, with functions, what other type could be possible?
void foo(){}
&foo; // #1
foo; // #2
Let's imagine that only #2 gives the type void(*)(), what would the type of &foo be? There is no other possibility.

Why is the dereference operator (*) also used to declare a pointer?

I'm not sure if this is a proper programming question, but it's something that has always bothered me, and I wonder if I'm the only one.
When initially learning C++, I understood the concept of references, but pointers had me confused. Why, you ask? Because of how you declare a pointer.
Consider the following:
void foo(int* bar)
{
}
int main()
{
int x = 5;
int* y = NULL;
y = &x;
*y = 15;
foo(y);
}
The function foo(int*) takes an int pointer as parameter. Since I've declared y as int pointer, I can pass y to foo, but when first learning C++ I associated the * symbol with dereferencing, as such I figured a dereferenced int needed to be passed. I would try to pass *y into foo, which obviously doesn't work.
Wouldn't it have been easier to have a separate operator for declaring a pointer? (or for dereferencing). For example:
void test(int# x)
{
}
In The Development of the C Language, Dennis Ritchie explains his reasoning thusly:
The second innovation that most clearly distinguishes C from its
predecessors is this fuller type structure and especially its
expression in the syntax of declarations... given an object of any
type, it should be possible to describe a new object that gathers
several into an array, yields it from a function, or is a pointer to
it.... [This] led to a
declaration syntax for names mirroring that of the expression syntax
in which the names typically appear. Thus,
int i, *pi, **ppi; declare an integer, a pointer to an integer, a
pointer to a pointer to an integer. The syntax of these declarations
reflects the observation that i, *pi, and **ppi all yield an int type
when used in an expression.
Similarly, int f(), *f(), (*f)(); declare
a function returning an integer, a function returning a pointer to an
integer, a pointer to a function returning an integer. int *api[10],
(*pai)[10]; declare an array of pointers to integers, and a pointer to
an array of integers.
In all these cases the declaration of a
variable resembles its usage in an expression whose type is the one
named at the head of the declaration.
An accident of syntax contributed to the perceived complexity of the
language. The indirection operator, spelled * in C, is syntactically a
unary prefix operator, just as in BCPL and B. This works well in
simple expressions, but in more complex cases, parentheses are
required to direct the parsing. For example, to distinguish
indirection through the value returned by a function from calling a
function designated by a pointer, one writes *fp() and (*pf)()
respectively. The style used in expressions carries through to
declarations, so the names might be declared
int *fp(); int (*pf)();
In more ornate but still realistic cases,
things become worse: int *(*pfp)(); is a pointer to a function
returning a pointer to an integer.
There are two effects occurring.
Most important, C has a relatively rich set of ways of describing
types (compared, say, with Pascal). Declarations in languages as
expressive as C—Algol 68, for example—describe objects equally hard to
understand, simply because the objects themselves are complex. A
second effect owes to details of the syntax. Declarations in C must be
read in an `inside-out' style that many find difficult to grasp.
Sethi [Sethi 81] observed that many of the nested
declarations and expressions would become simpler if the indirection
operator had been taken as a postfix operator instead of prefix, but
by then it was too late to change.
The reason is clearer if you write it like this:
int x, *y;
That is, both x and *y are ints. Thus y is an int *.
That is a language decision that predates C++, as C++ inherited it from C. I once heard that the motivation was that the declaration and the use would be equivalent, that is, given a declaration int *p; the expression *p is of type int in the same way that with int i; the expression i is of type int.
Because the committee, and those that developed C++ in the decades before its standardisation, decided that * should retain its original three meanings:
A pointer type
The dereference operator
Multiplication
You're right to suggest that the multiple meanings of * (and, similarly, &) are confusing. I've been of the opinion for some years that it they are a significant barrier to understanding for language newcomers.
Why not choose another symbol for C++?
Backwards-compatibility is the root cause... best to re-use existing symbols in a new context than to break C programs by translating previously-not-operators into new meanings.
Why not choose another symbol for C?
It's impossible to know for sure, but there are several arguments that can be — and have been — made. Foremost is the idea that:
when [an] identifier appears in an expression of the same form as the declarator, it yields an object of the specified type. {K&R, p216}
This is also why C programmers tend to[citation needed] prefer aligning their asterisks to the right rather than to the left, i.e.:
int *ptr1; // roughly C-style
int* ptr2; // roughly C++-style
though both varieties are found in programs of both languages, varyingly.
Page 65 of Expert C Programming: Deep C Secrets includes the following: And then, there is the C philosophy that the declaration of an object should look like its use.
Page 216 of The C Programming Language, 2nd edition (aka K&R) includes: A declarator is read as an assertion that when its identifier appears in an expression of the same form as the declarator, it yields an object of the specified type.
I prefer the way van der Linden puts it.
Haha, I feel your pain, I had the exact same problem.
I thought a pointer should be declared as &int because it makes sense that a pointer is an address of something.
After a while I thought for myself, every type in C has to be read backwards, like
int * const a
is for me
a constant something, when dereferenced equals an int.
Something that has to be dereferenced, has to be a pointer.

Why do we use "type * var" instead of "type & var" when defining a pointer?

I'm relatively new to C++ (about one year of experience, on and off). I'm curious about what led to the decision of type * name as the syntax for defining pointers. It seems to me that the syntax should be type & name as the & symbol is used everywhere else in code to refer to the variable's memory address. So, to use the traditional example of int pointers:
int a = 1;
int * b = &a;
would become
int a = 1;
int & b = &a
I'm sure there's some reason for this that I'm just not seeing, and I'd love to hear some input from C++ veterans.
Thanks,
-S
C++ adopts the C syntax. As revealed in "The Development of the C Language" (by Dennis Ritchie) C uses * for pointers in type declarations because it was decided that type syntax should follow use.
For each object of [a compound type], there was already a way to mention the underlying object: index the array, call the function, use the indirection operator [*] on the pointer. Analogical reasoning led to a declaration syntax for names mirroring that of the expression syntax in which the names typically appear. Thus,
int i, *pi, **ppi;
declare an integer, a pointer to an integer, a pointer to a pointer to an integer. The syntax of these declarations reflects the observation that i, *pi, and **ppi all yield an int type when used in an expression.
Here's a more complex example:
int *(*foo)[4][];
This declaration means an expression *(*foo)[4][0] has type int, and from that (and that [] has higher precedence than unary *) you can decode the type: foo is a pointer to an array of size 4 of array of pointers to ints.
This syntax was adopted in C++ for compatibility with C. Also, don't forget that C++ has a use for & in declarations.
int & b = a;
The above line means a reference variable refering to another variable of type int. The difference between a reference and pointer roughly is that references are initialized only, and you can not change where they point, and finally they are always dereferenced automatically.
int x = 5, y = 10;
int& r = x;
int sum = r + y; // you do not need to say '*r' automatically dereferenced.
r = y; // WRONG, 'r' can only have one thing pointing at during its life, only at its infancy ;)
I think that Dennis Ritchie answered this in The Development of the C Language:
For each object of such a composed
type, there was already a way to
mention the underlying object: index
the array, call the function, use the
indirection operator on the pointer.
Analogical reasoning led to a
declaration syntax for names mirroring
that of the expression syntax in which
the names typically appear. Thus,
int i, *pi, **ppi;
declare an integer, a pointer to an
integer, a pointer to a pointer to an
integer. The syntax of these
declarations reflects the observation
that i, *pi, and **ppi all yield an
int type when used in an expression.
Similarly,
int f(), *f(), (*f)();
declare a function returning an
integer, a function returning a
pointer to an integer, a pointer to a
function returning an integer;
int *api[10], (*pai)[10];
declare an array of pointers to
integers, and a pointer to an array of
integers. In all these cases the
declaration of a variable resembles
its usage in an expression whose type
is the one named at the head of the
declaration.
So we use type * var to declare a pointer because this allows the declaration to mirror the usage (dereferencing) of the pointer.
In this article, Ritchie also recounts that in "NB", an extended version of the "B" programming language, he used int pointer[] to declare a pointer to an int, as opposed to int array[10] to declare an array of ints.
If you are a visual thinker, it may help to imagine the asterisk as a black hole leading to the data value. Hence, it is a pointer.
The ampersand is the opposite end of the hole, think of it as an unraveled asterisk or a spaceship wobbling about in an erratic course as the pilot gets over the transition coming out of the black hole.
I remember being very confused by C++ overloading the meaning of the ampersand, to give us references. In their desperate attempt to avoid using any more characters, which was justified by the international audience using C and known issues with keyboard limitations, they added a major source of confusion.
One thing that may help in C++ is to think of references as pre-prepared dereferenced pointers. Rather than using &someVariable when you pass in an argument, you've already used the trailing ampersand when you defined someVariable. Then again, that might just confuse you further!
One of my pet hates, which I was unhappy to see promulgated in Apple's Objective-C samples, is the layout style int *someIntPointer instead of int* someIntPointer
IMHO, keeping the asterisk with the variable is an old-fashioned C approach emphasizing the mechanics of how you define the variable, over its data type.
The data type of someIntPointer is literally a pointer to an integer and the declaration should reflect that. This does lead to the requirement that you declare one variable per line, to avoid subtle bugs such as:
int* a, b; // b is a straight int, was that our intention?
int *a, *b; // old-style C declaring two pointers
int* a;
int* b; // b is another pointer to an int
Whilst people argue that the ability to declare mixed pointers and values on the same line, intentionally, is a powerful feature, I've seen it lead to subtle bugs and confusion.
Your second example is not valid C code, only C++ code. The difference is that one is a pointer, whereas the other is a reference.
On the right-hand side the '&' always means address-of. In a definition it indicates that the variable is a reference.
On the right-hand side the '*' always means value-at-address. In a definition it indicates that the variable is a pointer.
References and pointers are similar, but not the same. This article addresses the differences.
Instead of reading int* b as "b is a pointer to int", read it as int *b: "*b is an int". Then, you have & as an anti-*: *b is an int. The address of *b is &*b, or just b.
I think the answer may well be "because that's the way K&R did it."
K&R are the ones who decided what the C syntax for declaring pointers was.
It's not int & x; instead of int * x; because that's the way the language was defined by the guys who made it up -- K&R.