25

The following declaration in C:

int* a, b;

will declare a as type int* and b as type int. I'm well aware of this trap, but what I want to know is why it works this way. Why doesn't it also declare b as int*, as most people would intuitively expect? In other words, why does * apply to the variable name, rather than the type?

Sure you could write it this way to be more consistent with how it actually works:

int *a, b;

However, I and everyone I've spoken to think in terms of a is of type "pointer to int", rather than a is a pointer to some data and the type of that data is "int".

Was this simply a bad decision by the designers of C or is there some good reason why it's parsed this way? I'm sure the question has been answered before, but I can't seem to find it using the search.

4
  • 9
    To be honest, if the C language made the * modify the type then we would have people asking "why doesn't int *a, b; give me a pointer and an integer?". Commented Jul 15, 2010 at 23:46
  • 2
    @bta, yes, we would - but I think it would have confused fewer people. Anyway, the point isn't to argue about the decision that's been made 40 years ago, it's just to understand whether there are reasons for it that I'm not aware of. Commented Jul 16, 2010 at 0:11
  • You might want to read this c-faq.com/decl/charstarws.html Commented Jul 16, 2010 at 6:34
  • @bta That way funny and clever 🤣, maybe it would be better that * sticks to its f*cking type! Commented Aug 14 at 16:11

7 Answers 7

35

C declarations were written this way so that "declaration mirrors use". This is why you declare arrays like this:

int a[10];

Were you to instead have the rule you propose, where it is always

type identifier, identifier, identifier, ... ;

...then arrays would logically have to be declared like this:

int[10] a;

which is fine, but doesn't mirror how you use a. Note that this holds for functions, too - we declare functions like this:

void foo(int a, char *b);

rather than

void(int a, char* b) foo;

In general, the "declaration mirrors use" rule means that you only have to remember one set of associativity rules, which apply to both operators like *, [] and () when you're using the value, and the corresponding tokens in declarators like *, [] and ().


After some further thought, I think it's also worth pointing out that spelling "pointer to int" as "int*" is only a consequence of "declaration mirrors use" anyway. If you were going to use another style of declaration, it would probably make more sense to spell "pointer to int" as "&int", or something completely different like "@int".

Sign up to request clarification or add additional context in comments.

3 Comments

Hmm, this doesn't sound like a very good reason to me. I do want to declare arrays as int[10], which is how Java and C# do it. Are there other examples in the C syntax of declaration mirroring use?
@Evgeny, C is a very different language from Java. A Java array is totally different. The array variable holds only a modifiable reference to an array. A C array variable is the array. Another example of "declaration mirrors use" is a declaration of a function returning a pointer to an integer. int *foo(int bar);. This reflects the fact that *foo(3) is an int. I suggest you read K&R C 2nd ed., where this is explained.
@Evgeny: The most obvious other example is function declarations, which are written to look like the function call.
22

There's a web page on The Development of the C Language that says, "The syntax of these declarations reflects the observation that i, *pi, and **ppi all yield an int type when used in an expression." Search for that sentence on the page to find the relevant section that talks about this question.

Comments

11

There may be an additional historical reason, but I've always understood it this way:

One declaration, one type.

If a, b, c, and d must be the same type here:

int a, b, c, d;

Then everything on the line must an integer as well.

int a, *b, **c, ***d;

The 4 integers:

  1. a
  2. *b
  3. **c
  4. ***d

It may be related to operator precedence, as well, or it may have been at some point in the past.

7 Comments

The question wasn't how you understood it.
@georg: it's just a different wording of the above two. how did you understand it?
@Georg, eruciform is correct. From K&R C, "The declaration of the pointer ip, int *ip, is intended as a mnemonic; it says that the expression *ip is an int. The syntax of the declaration for a variable mimics the syntax of expressions in which the variable might appear. This is the reason for the syntax.
eruciform, @matthew, the question asks e.g. "why it's parsed this way", i.e. why was the language designed that way. I only see "here is how i understand the syntax" in this answer :)
@Georg, he never said "here is how i understand the syntax" The understanding he gave of the language's design is the real reason for the language's design.
|
2

I assume it is related to the full declaration syntax for type modifiers:

int x[20], y;
int (*fp)(), z;

In these examples, it feels much more obvious that the modifiers are only affecting one of the declarations. One guess is that once K&R decided to design modifiers this way, it felt "correct" to have modifiers only affect one declaration.

On a side note, I would recommend just limiting yourself to one variable per declaration:

int *x;
int y;

2 Comments

Yeah, it is more obvious here, but really, who the hell declares a function pointer and an int in the same statement? :)
@Evgeny - you are missing the point. For function pointer syntax it's easy to understand why the modifiers only affect one variable. If the language was inconsistent and some modifiers affects one variable one other modifiers affected all variable, that would be bad design.
1

The * modifies the variable name, not the type specifier. This is mostly because of the way the * is parsed. Take these statements:

char*  x;
char  *x;

Those statements are equivalent. The * operator needs to be between the type specifier and the variable name (it is treated like an infix operator), but it can go on either side of the space. Given this, the declaration

int*  a, b;

would not make b a pointer, because there is no * adjacent to it. The * only operates on the objects on either side of it.

Also, think about it this way: when you write the declaration int x;, you are indicating that x is an integer. If y is a pointer to an integer, then *y is an integer. When you write int *y;, you are indicating that *y is an integer (which is what you want). In the statement char a, *b, ***c;, you are indicating that the variable a, the dereferenced value of b, and the triply-dereferenced value of c are all of type char. Declaring variables in this way makes the usage of the star operator (nearly) consistent with dereferencing.

I agree that it would almost make more sense for it to be the other way around. To avoid this trap, I made myself a rule always to declare pointers on a line by themselves.

1 Comment

That is the status-quo, but why is it that way?
1

Consider the declaration:

int *a[10];
int (*b)[10];

The first is an array of ten pointers to integers, the second is a pointer to an array of ten integers.

Now, if the * was attached to the type declaration, it wouldn't be syntatically valid to put a parenthesis between them. So you'd have to find another way to differentiate between the two forms.

2 Comments

"It should be noted that the declaration of a pointer to an array is not that useful" What? With int (*b)[10], you can assign b to point to e.g. an arbitrary element of int ar[ROW_COUNT][10], and pointer arithmetic will operate by row (e.g. after ar += 1, ar is incremented by one row). Please explain how to do that with a "simple pointer" (not sure what "simple" actually means).
You're right, I was thinking more about the use of that pointer as a parameter and failed to consider the case you mentioned. Correcting now.
0

Because if the statement

int* a, b;

were to declare b as a pointer too, then you would have no way to declare

int* a;
int  b;

on a single line.

On the other hand, you can do

int*a, *b;

to get what you want.

Think about it like that: the way it is now it is still the most concise and yet unique way to do it. That's what C is mostly about :)

1 Comment

Yes, well, similarly you can't declare an int and a float on the same line - so what? Actually, you can't delcare them in the same statement, but you could have a line that said int* a; int b; - no problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.