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
int i;
the declarator is just i
, so I'm saying that i
is going to be an int
. No mystery there. But if I say
int a[10];
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
int f();
I'm saying that calling the function is going to return an int
.
Finally, if I say
int *ip;
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 int
.
And of course I can string these together on the same line:
int i, a[10], 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, and they can all be declared (in a general and open-ended way) using the same sort of concise, punctuation-rich, unwordy syntax that C is famous for
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.
See also Question 1.21 in the C FAQ list.
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 x1
, x2
, and 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. (Part of the problem is that pointers do not necessarily make good "opaque" types. If you're dealing with a pointer, you often need to know that you're dealing with a pointer, so hiding it behind a typedef doesn't help.)