when I declare C++ variables, I do it like this:
int a,b,c,d;
or
string strA,strB,strC,strD;
I.e., first
C has a rule for its declarations: Declarations mimic use. What this means is that the declaration for a variable looks like the way the variable is used. For example:
int i;
Using this variable looks like i
, and that expression results in an int
.
int *i;
Using this variable looks like *i
, and that expression results in an int
. This also implies that an expression i
results in a pointer to an int
.
int i(void);
Using this variable looks like i()
, and results in an int
. This also implies i
is a function that takes nothing and that returns an int
.
int (*i[5])();
Using this variable looks like (*i[x])()
, and results in an int
. This also implies *i[x]
is a function returning int
, and i[x]
is a pointer to a function returning int
, and i
is an array of pointers to functions returning int
.
So:
int a, *b, c(void), (*d[5])(void);
declares several expressions which all have the type int
, but the variables themselves are not all int
s.
N.B. in declarations of function and arrays the sub declarations don't literally resemble the use of the variable. I.e. int i[5];
doesn't mean that you need to put a '5' inside the square brackets when you use i
, and int i(int);
doesn't mean you call the function by saying i(int);
. Also declarations of variables of struct types don't match their use since you'd have to declare expressions for each member variable.
Related to the syntax for declaring variables is that of typedefs. To declare a typedef with a certain type, you use the same syntax as for declaring a variable of the desired type, but you stick typedef
in front of the declaration and the variable name becomes the typedef name.
typedef int (*array_of_function_pointers[5])(void);
C++ adds references, templates, pointers to member, and a bunch of stuff. It tries to some extent to follow the old C convention, but that convention doesn't really work all that well for many of the things C++ adds, so you do start getting inconsistencies. You'll just have to learn the idiosyncrasies and chalk it up to an imperfect marriage between C and C++.
The reason is: in reality &
and *
in C and C++ applies to variable and no to type.
So the problem is that if you want to declare 4 pointer to integers you should write
int *a, *b, *c, *d;
Writing int* a, b;
means
int *a;
int b;
For this reason a lot of people prefer to write int *a;
than int* a;
, just code style.
Of course int*
is a type, but the C syntax states clearly that in a variable declaration *
and &
will apply to variable.
This happens for the same reason that arrays in C are declared in this way:
int a[10], b;
std::cout << typeid(a).name() << std::endl; // it will print int[10]
std::cout << typeid(b).name() << std::endl; // it will print int
In other languages like C# instead the syntax int[] array;
is used.
Another thing is the pointer to function syntax:
// Now we declare a variable that is a pointer to function
int (*pt2Function)(char, char) = NULL;
We are applying the *
to the variable name.
It seems to me that the use of *
and &
applied to the variable, also if 90% of us would prefer differently, is more consistent with the rest of the language.
In the same way, going back to arrays:
// Now we declare a variable that is a pointer to an array of 10 integers.
int (*arrayptr)[10];
C++ has inherited this syntax from C. The K&R book (§5.1) says that the expression
int *ip, **ipp;
is intended to be mnemonic (i.e. you read “*ip
and **ipp
have type int
”), they wanted it to mimic usage syntax.
I agree it is quite cryptic and counterintuitive for humans, and inconsistent with typedefs, e.g.
typedef int* intptr;
intptr a, b; // equivalent to: int *a, *b
but it never really was C++ author's decision, the choice to make the language compatible with C implied using this syntax, that's why.
It's because of the C heritage. The *
modifier applies to the variable in C. So the C++ designers made &
to apply to the variable as well by analogy, since they couldn't change the first without breaking C compatibility. Same is true for the array syntax too:
int anInt, *aPointerToInt, anArrayOfInt[100];
In The Design and Evolution of C++ Bjarne Stroustrup says he wasn't happy with this but had to accept it for C compatibility. He was unhappy with this in particular:
int *a[10];
It's not clear from the declaration if a
is a pointer to an array or an array of pointers (it's an array of pointers, you need brackets to override).
Of course, you can always use a typedef
to clean things up a little.
The best answer I've seen as to why things like *
apply to variables and not to types is the idea that declaration should follow use.
Basically, how you declare a variable should look similar to how you use a variable.
For example,
int *a, b;
...
*a = 42;
b = 314159;
...the use of a
and b
looks similar to the declaration of a
and b
.
There's even a citation for this behavior from Dennis Ritchie, one of the creators of C:
Analogical reasoning led to a declaration syntax for names mirroring that of the expression syntax in which the names typically appear...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.