3

Beginners are often astonished to learn that the declaration

int* a, b;

declares a as a pointer to int and b as an int. This stems from the curious way in which declarations in C and C++ are divided into specifiers and declarators. A declaration begins with a single list of specifiers, but each entity declared has its own declarator, which independently modifies the specifier in order to give the resulting type of the entity. The above declaration therefore has two declarators, *a and b. The * in the first declarator combines with the int specifier to give the type int* for a, and the second declarator has no modifiers, so the resulting type is simply int.

At the same time, in certain contexts a type name is expected (type-id in C++) which is the combination of specifiers and optionally an "abstract declarator", which is basically a declarator that omits the name. int* for example is a type name. But a type name is not directly used in declarations. As we see above, int* a, b is not interpreted as a type name followed by two identifiers, as one might expect.

So we have specifiers, declarators, and combinations of the two forming type names, but type names aren't used to declare variables of the corresponding types, even though they are literally the names of types, defined to only lack a name. What are the historical reasons for the grammar being this way, complicated and counterintuitive? Is there some benefit to being able to write code like this?

int *a, (*b)();
Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • 5
    Is this an honest question, or karma bait? You give a lot of nice information, but I'm not sure what you expect to get from this. – Michael Dorgan Apr 28 '15 at 21:21
  • 4
    It's an honest question. I'm interested in the historical reasons for the grammar being the way it is. – Brian Bi Apr 28 '15 at 21:21
  • 4
    [The Development of the C Language](http://cm.bell-labs.com/who/dmr/chist.html) gives some context: *"Analogical reasoning led to a declaration syntax for names mirroring that of the expression syntax in which the names typically appear."* and *"An accident of syntax contributed to the perceived complexity of the language."* – dyp Apr 28 '15 at 21:21
  • 6
    Why opinion based? There could be documented reasons for this. I'm voting to re-open. – juanchopanza Apr 28 '15 at 21:23
  • 3
    @dyp your link is dead already :( – Degustaf Apr 28 '15 at 21:23
  • 1
    @Degustaf I was just reading it... must have been caching :( I've literally just copied the quotes from it. – dyp Apr 28 '15 at 21:24
  • 1
    One solution is to declare one variable per line, which resolves your first example. – Thomas Matthews Apr 28 '15 at 21:26
  • 5
    @ThomasMatthews: A history question doesn't have a "solution" – Mooing Duck Apr 28 '15 at 21:26
  • @dyp I'm familiar with the whole "declaration mirrors usage" thing, and I even think it's a good idea. But I think that is in a way orthogonal to what's going on here. We could on one hand say that the declaration `int* a;` is justified by that paradigm---`*` both declares and dereferences a pointer---and on the other hand still ask why `int* a, b;` is not the same as `int* a; int* b;` – Brian Bi Apr 28 '15 at 21:27
  • 1
    @MooingDuck: I'm referencing complaints, not the history issue. IMO, there is more complaining than history question. – Thomas Matthews Apr 28 '15 at 21:28
  • 1
    It was an interesting experiment, but I think it failed. It seems the folks who did it think it failed as well, as evidenced by the fact that they abandoned that syntax for a simpler one in Go. – Lee Daniel Crocker Apr 28 '15 at 21:28
  • 3
    From [the StackOverflow online help](http://stackoverflow.com/help/dont-ask): "You should only ask practical, answerable questions based on actual problems that you face. Chatty, open-ended questions diminish the usefulness of our site and push other questions off the front page." – Paul R Apr 28 '15 at 21:28
  • btw, I apologize for the linkbaity title. It wasn't my intent at all. – Brian Bi Apr 28 '15 at 21:28
  • A type-id never contains a declarator. It may contain an abstract-declarator. – Columbo Apr 28 '15 at 21:31
  • @Columbo whoops, thanks, I'll edit. – Brian Bi Apr 28 '15 at 21:31
  • 3
    Also, C++ is not a language for beginners. It's hard, ludicrously complicated and takes years to master, but it compensates for all that by what it has to offer. Even if we change the syntax to something else, if we want to maintain its flexibility, we're gonna end up where we started. However, we would never want to drop flexibility for novice-friendliness. – Columbo Apr 28 '15 at 21:33
  • I don't advocate changing the syntax. However, I have a suspicion that even experts are confused by why it is the way it is in the first place. – Brian Bi Apr 28 '15 at 21:33
  • OK, the document is available [here](https://www4.cs.fau.de/Services/Doc/C/C/history.pdf) as a PDF. – dyp Apr 28 '15 at 21:33
  • @Brian Well, if it wasn't so complicated and unreadable, it wouldn't be so powerful either, would it? – Columbo Apr 28 '15 at 21:35
  • @Columbo So then I think there is a question of whether that additional power is useful enough to justify its complexity. C++ certainly tends to favour the side of additional power, but even still there is certainly a tradeoff, one which plays out every time additional features are proposed for the core language. Now what's the additional power that the current system gives you? Well, you can write code like what is shown in the last line of the question, and declare a pointer and a pointer to function in the same declaration. How compelling is that? – Brian Bi Apr 28 '15 at 21:38
  • 5
    @Columbo I think that argument (it needs to be complex to be flexible) is a fallacy. You can very easily implement all type operators via alias templates, e.g. `pointer` without even altering the language. The difficulties seem to arise from the combinations of prefix and postfix operators (as pointed out by Stroustrup I think in D&E). – dyp Apr 28 '15 at 21:40
  • @Columbo It doesn't have to be complicated unreadable to be powerful. It is complicated and unreadable because it is impossible to fix mistakes without breaking decades of backwards compatibility. – juanchopanza Apr 28 '15 at 21:40
  • @dyp: I've been getting an "Object not found" on [that link](http://cm.bell-labs.com/who/dmr/chist.html) for at least the last few days. Are you able to access it? (I *hope* they haven't removed Dennis Ritchie's old home page.) – Keith Thompson Apr 28 '15 at 21:47
  • @KeithThompson Unfortunately, I'm not able to access it either. However, google found the page as a PDF somewhere else. I've linked it above: https://www4.cs.fau.de/Services/Doc/C/C/history.pdf – dyp Apr 28 '15 at 21:48
  • 3
    Related/duplicate: [Why is the C++ variable declaration syntax inconsistent?](http://stackoverflow.com/questions/7967594) and [Pointer syntax in C: why does * only apply to the first variable?](http://stackoverflow.com/q/3260784/) – dyp Apr 28 '15 at 21:49
  • 2
    If `int* a, b;` made both variables pointers, then people would complain about `int *a, b;`. – user253751 Apr 28 '15 at 21:55
  • 2
    @dyp: Good news, everyone! It's just been moved to http://cm.bell-labs.co/who/dmr/. Replace dmr by bwk for Brian Kernighan's pand and by ken for Ken Thompson's. – Keith Thompson Apr 29 '15 at 00:16
  • 1
    @dyp: Well, maybe. On further investigation, it looks like cm.bell-labs.co is a mirror of cm.bell-labs.com, not maintained by Bell Labs or by Alcatel-Lucent (the current owner of Bell Labs). Inquiries are being made; see the comp.lang.c newsgroup for more information. I'll comment here again when/if I have more information. – Keith Thompson Apr 29 '15 at 03:28
  • @KeithThompson: I've had that page disappear on me more than once in the past, and then it magically re-appears later on. May just be a site maintenance issue. – John Bode Apr 29 '15 at 14:27
  • 2
    @Brian: Does it bother you that `int a[10], b` only declares `a` as an array of `int`? It's the *exact same concept*; the "array-ness" of `a` is specified in the declarator, not the type specifier. Same thing for `int f(), g;` - only `f` is a function returning `int`. The "function-ness" of `f` is part of the declarator, not the type specifier. Why should "pointer-ness" be any different? Just because it's a unary (prefix) operation? Would you rather see declaration syntax of `int[N] a;` or `int() f;`? – John Bode Apr 29 '15 at 14:39
  • @JohnBode Well, I think the idea of having declaration follow usage is reasonable, so I can see why `int a[10]` makes sense. If I were making the rules, I would probably just force `a` and `b` to be declared in separate declarations. – Brian Bi Apr 29 '15 at 17:15

0 Answers0