This is not a duplicate of this question
I understand why it is better to declare variables like this:
I understand that this avoids the ambiguous case of
int* var1, var2 where it would seem that
var2 are both pointers to integers, but var2 is actually just a regular integer.
My question is why
int* var1, var2 doesn’t declare both variables as pointers to integers. Intuitively, I would think that
int* would act like any other data type, where you would just have to use
int* var1, var2 to declare both variables of type
int*. In other words, why is C designed so that each variable needs an asterisk before it?
Intuitively, I would think that
int*would act like any other data type
If you want to become comfortable with C, you may need to adjust your intuition! Types are at least a little bit complicated; in fact the type system is a lot like a little programming language of its own!
int is a type, but "pointer" is a meta-type. You can have pointers to anything, even pointers to pointers.
In other words, why is C designed so that each [pointer] variable needs an asterisk before it?
Because, as explained in several of the answers to that other question, the scheme is that a declaration contains a "base type" and then some "declarators". In the case of arrays, pointers, and functions, the "declarator" is a little sample of whatever thing is going to have the base type. If I say
the declarator is just
i, so I’m saying that
i is going to be an
int. No mystery there. But if I say
I’m saying that one element of the array I’m declaring — that is, when I later use the array by saying something like
a[i] — is going to be an
int. If I say
I’m saying that calling the function is going to return an
Finally, if I say
I’m saying that when I take the contents of this pointer, using the expression
*ip, then what
*ip is going to be is an
And of course I can string these together on the same line:
int i, a, f(), *ip;
Now, perhaps I’m just rehashing the answers at the other question, and not explaining why things were set up this way. The reason is that it makes it possible to declare arbitrarily-complicated derived types. We can have arrays, and pointers, and functions, and arrays of arrays, and pointers to arrays, and functions returning pointers, and pointers to functions, and arrays of pointers to functions, and functions returning pointers to functions, and types even more numerous and complicated than those.
I don’t think it would have been possible to design a type system that could have expressed the concept of "array of pointers to functions" on the left, leaving you to just put the variable name on the right. Or, if you could design such a system, it would have been complicated and confusing and cumbersome to use, even more complicated and confusing than C’s present scheme seems to you, with its odd combination of type names on the left, and declarators — with their mixture of names, asterisks, and other punctuation — on the right.
Or, perhaps I lied. I just said, "I don’t think it would have been possible to design a type system that could have expressed [complicated things] on the left, leaving you to just put the variable name on the right." But, in fact, C’s
typedef facility lets you do precisely that, if you want to build up a complex type in stages.
For example, if you say
typedef int *pointer_to_int;
you have arranged that the identifier
pointer_to_int is not an actual variable of type
int *, but rather, a synonym for the type
int *. Having done that, you can declare several integer pointers at once, without having to re-type those pesky asterisks:
pointer_to_int p1, p2, p3; /* just like int *p1, *p2, *p3; */
And it works for more complicated types, too. I can say
typedef int *(*pointer_to_function_returning_pointer_to_int)();
and then do
pointer_to_function_returning_pointer_to_int x1, x2, x3;
at which point
x3 are all pointers to functions returning pointers to
int, just as if I’d said
int *(*x1)(), *(*x2)(), *(*x3)();
In fact, the CS50 course introduces a "handy" typedef
typedef char *string;
which makes the identifier
string be an alias for the type "pointer to
char", which allegedly makes it easier for beginners to declare lots of strings. Unfortunately,
char * isn’t really C’s One True String type, so the
string typedef probably causes as much confusion as it attempts to resolve.