3

There're a lot of questions on SO about details of pointer and array declarations in C (and C subset of C++).
I'm more interested in why.
Why do we have to put *, [] in front of every variable when we declare several pointers/arrays in a row?

int *a, *b;
int c[1], d[1];

Why do we have to type out things after/around variable names in function pointers?

void (*foo_ptr)(int, int);

Why do we have this feature that confuses a lot of newcomers, when even compilers recognize and report these things as part of type? Ex: function foo accepts int** but it was given int*

I guess I'm looking for intuition behind it that caused it being created this way, so that I can apply it to my understanding of the language. Right now I just don't see it...

Noone AtAll
  • 375
  • 1
  • 10
  • @Mat that's consequence of pointer symbol being next to name, not a cause. I believe in std::function from cpp it was solved via `void*(int, int)` vs `void(int, int)*` – Noone AtAll Dec 21 '19 at 13:14
  • You do not have to; you are free to declare `typedef int *IntPointer; typedef int IntArrayOf1Element[1];` and then use `IntPointer a, b; IntArrayOf1Element c, d;`. – Eric Postpischil Dec 21 '19 at 13:15
  • @EricPostpischil I'm asking for intuition/logic behind language design. I know that there are workarounds, but that's not what I'm looking for. – Noone AtAll Dec 21 '19 at 13:20
  • 1
    The logic is that a declaration declares a list of things; `int X, Y, Z;` declares `X`, `Y`, and `Z` to be `int`, and each of `X`, `Y`, and `Z` is a “picture” of some expression, such as `b`, `*b`, `b[10]`, `*b[10]`, and so on. The actual type for the declared identifier is derived from the picture: Since `*b[10]` is an `int`, then `b[10]` is a pointer to an `int`, so `b` is an array of 10 pointers to `int`. – Eric Postpischil Dec 21 '19 at 13:22
  • @EricPostpischil Interesting proposal. I would've even accepted this intuition as an answer (if you make it an answer and not a comment). The only hole in your theory I see is that `int a = 0, *b = 0` can't be changed to `int X = 0, Y = 0` as easily as you suggest. But it feels close to truth. – Noone AtAll Dec 21 '19 at 13:38
  • 1
    @NooneAtAll: How do those not easily fit the pattern? In `int X = 0, Y = 0`, `X` is the “picture” `a`, and `Y` is the “picture” “*b”. – Eric Postpischil Dec 21 '19 at 14:00
  • @EricPostpischil after `int a = 0, *b = 0` you'll have and int named `a` that is 0 and null-pointer `b`. If you use `a` you'd get 0. If you use `*b` you would not get 0, but segfault. The `int X=0, Y=0` suggests that X and Y supposed to have same usability. – Noone AtAll Dec 21 '19 at 14:04
  • @NooneAtAll: Only the `X` part is a picture; the `= initial value` is separate, and it specifies an initial value for the thing being declared, not an initial value for the expression pictured. – Eric Postpischil Dec 21 '19 at 14:08
  • @EricPostpischil is right, it is a historical reason from K&R. For the C authors, in expression `int * p;`, the type is `int` and the variable is `*p`. I also think that it would have been much more logical to say that `int*` is the type and `p` the variable. This is how compilers handle them. BTW, in C# `int[] a, b;` declares 2 arrays of integers. – prapin Oct 24 '20 at 18:26
  • @EricPostpischil Your link tries to compile on a C++ compiler... I had tried the syntax on a C# source code to be sure before posting. – prapin Oct 24 '20 at 18:38
  • @prapin: Oops, my mistake. – Eric Postpischil Oct 24 '20 at 19:03

2 Answers2

9

Kernighan and Ritchie write, in The C Programming Language, 1978, page 90:

The declaration of the pointer px is new.

int *px;

is intended as a mnemonic; it says the combination *px is an int, that is, if px occurs in the context *px, it is equivalent to a variable of the type int. In effect, the syntax of the declaration for a variable mimics the syntax of expressions in which the variable might appear. This reasoning is useful in all cases involving complicated declarations. For example,

double atof(), *dp;

says that in an expression atof() and *dp have values of type double.

Thus, we see that, in declarations such as int X, Y, Z, X, Y, and Z give us “pictures” of expressions, such as b, *b, b[10], *b[10], and so on. The actual type for the declared identifier is derived from the picture: Since *b[10] is an int, then b[10] is a pointer to an int, so b is an array of 10 pointers to int.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

Why do we have to put *, [] in front of every variable when we declare several pointers/arrays in a row?

The answer would be: explicit is better than implicit. In such a case we can even declare different types on the same row, int *a, *b, c; and so on, in another case it would be too messy. The same true for the second question.

funnydman
  • 9,083
  • 4
  • 40
  • 55
  • why would "declaring different types on the same row" be a good thing? why explicitly stating type "pointer of int" once is worse than stating "this line is for ints and this variable is a pointer, this variable is a pointer, this variable is a pointer"? – Noone AtAll Dec 21 '19 at 13:11
  • 1
    The pointer is just a variable that contains an address for an object with the type specified. If we would have "pointer of int", then we would need to create "special" pointer notation for every existing type. – funnydman Dec 21 '19 at 13:18
  • I think your comment gave me a revelation... But I don't have enough info nor trust in my imagination to confirm it. So, please, explain your reasons: why "variable that contains an address with the type" should be treated same way as "variable of the type"? (also isn't pointer notation *already* "special pointer notation for every existing type"???) – Noone AtAll Dec 21 '19 at 13:53
  • Here `int *pt` pt is a pointer (stores the address of some int). In this case, `int` notations mean that the pointer contains the address of different _int_. So, for example, `char *c` contains the address of some different _char_. So, writing `int a, *p` - we claim that the `a` has the type `int` itself, while `*p` refers to the int. It's was made to have things more consistent. I guess it would be much harder to deal with `int` and `PointerInt` types and so on... – funnydman Dec 21 '19 at 14:06