35

After having seen (and asked!) so many questions similar to

What does int (*f)(int (*a)[5]) mean in C?

and even seeing that they'd made a program to help people understand the C syntax, I can't help but wonder:

Why was the syntax of C designed this way?

For example, if I were designing pointers, I would translate "a pointer to a 10-element array of pointers" into

int*[10]* p;

and not

int* (*p)[10];

which I feel most people would agree is much less straightforward.

So I'm wondering, why the, uh, unintuitive syntax? Was there a specific problem the syntax solves (perhaps an ambiguity?) that I'm unaware of?

6
  • 2
    You know there are no real answer to this, and such, questions. Right? What's you'll get are just guesses. Oct 31, 2011 at 7:51
  • 10
    @VJo - there may well be a "real" (ie. objective) answer — language authors and standards committees alike have explicitly justified (or at least explained) many of these decisions.
    – detly
    Oct 31, 2011 at 8:22
  • I don't think your proposed syntax is necessarily more or less "intuitive" than C syntax. C is what it is; once you've learned it, you'll never have these questions again. If you haven't learned it... well, maybe that's the real problem.
    – Caleb
    Oct 31, 2011 at 19:45
  • 6
    @Caleb: Funny how you concluded that so easily, because I learned it and I still had this question...
    – user541686
    Oct 31, 2011 at 22:24
  • 1
    The cdecl command is very handy for decoding complex C declarations. There's also a web interface at cdecl.org. Nov 1, 2011 at 2:01

5 Answers 5

23

My understanding of the history of it is that it's based on two main points...

Firstly, the language authors preferred to make the syntax variable-centric rather than type-centric. That is, they wanted a programmer to look at the declaration and think "if I write the expression *func(arg), that'll result in an int; if I write *arg[N] I'll have a float" rather than "func must be a pointer to a function taking this and returning that".

The C entry on Wikipedia claims that:

Ritchie's idea was to declare identifiers in contexts resembling their use: "declaration reflects use".

...citing p122 of K&R2 which, alas, I don't have to hand to find the extended quote for you.

Secondly it is actually really, really difficult to come up with a syntax for declaration that is consistent when you're dealing with arbitrary levels of indirection. Your example might work well for expressing the type you thought up off-the-bat there, but does it scale to a function taking a pointer to an array of those types, and returning some other hideous mess? (Maybe it does, but did you check? Can you prove it?).

Remember, part of C's success is due to the fact that compilers were written for many different platforms, and so it might have been better to ignore some degree of readability for the sake of making compilers easier to write.

Having said that, I'm not an expert in language grammar or compiler writing. But I know enough to know there's a lot to know ;)

3
  • 6
    "making compilers easier to write"... except C is notorious for being hard to parse (only topped by C++).
    – Jan Hudec
    Nov 1, 2011 at 8:37
  • 2
    @JanHudec - Well... yeah. That's not a watertight statement. But while C is impossible to parse as a context-free grammar, once one person has come up with a way to parse it, this ceases to be the hard step. And the fact is, it was prolific in its early days due to people being able to bang out compilers easily, so K&R must have struck some balance. (In Richard Gabriel's infamous The Rise of "Worse is Better", he takes for granted — and bemoans — the fact that it's easy to write a C compiler for a new platform.)
    – detly
    Nov 1, 2011 at 8:47
  • I'm happy to be corrected on this, by the way — I don't know all that much about parsing and grammar. I'm going more on inference from historical fact.
    – detly
    Nov 1, 2011 at 8:50
15

A lot of the oddities of the C language can be explained by the way computers worked when it was designed. There were very limited amounts of storage memory, so it was very important to minimize the size of the source code files themselves. The programming practice back in the 70s and 80s was to make sure the source code contained as few characters as possible, and preferably no excessive source code comments.

This is of course ridiculous today, with pretty much unlimited storage space on hard drives. But it is part of the reason why C has such weird syntax in general.


Regarding array pointers specifically, your second example should be int (*p)[10]; (yeah the syntax is very confusing). I would perhaps read that as "int pointer to array of ten"... which makes sense somewhat. If not for the parenthesis, the compiler would interpret it as an array of ten pointers instead, which would give the declaration an entirely different meaning.

Since array pointers and function pointers both have quite obscure syntax in C, the sensible thing to do is to typedef away the weirdness. Perhaps like this:

Obscure example:

int func (int (*arr_ptr)[10])
{
  return 0;
}

int main()
{
  int array[10];
  int (*arr_ptr)[10]  = &array;
  int (*func_ptr)(int(*)[10]) = &func;

  func_ptr(arr_ptr);
}

Non-obscure, equivalent example:

typedef int array_t[10];
typedef int (*funcptr_t)(array_t*);


int func (array_t* arr_ptr)
{
  return 0;
}

int main()
{
  int        array[10];
  array_t*   arr_ptr  = &array; /* non-obscure array pointer */
  funcptr_t  func_ptr = &func;  /* non-obscure function pointer */
  
  func_ptr(arr_ptr);
}

Things can get even more obscure if you are dealing with arrays of function pointers. Or the most obscure of them all: functions returning function pointers (mildly useful). If you don't use typedefs for such things, you'll quickly go insane.

7
  • Ah, finally a reasonable answer. :-) I'm curious as to how the particular syntax would actually shrink source code size, but anyhow it's a plausible idea and makes sense. Thanks. +1
    – user541686
    Oct 31, 2011 at 8:10
  • 1
    I'd say it was less about source code size and more about compiler writing, but definitely +1 for "typdef away the weirdness". My mental health improved dramatically the day I realised I could do this.
    – detly
    Oct 31, 2011 at 8:33
  • 3
    [Citation needed] on the source code size thing. I've never heard of such a limitation (though maybe it's something "everybody knows"). Oct 31, 2011 at 12:47
  • 2
    Well I coded programs in the 70s in COBOL,Assembler,CORAL and PL/1 on IBM,DEC and XEROX kit and I never EVER came across a source code size limitation. Limitations on array size, executable size, program name size -- but never source code size. Nov 1, 2011 at 5:04
  • 2
    @Sean McMillan: I do not think source code size was a limitation (consider that at that time verbose languages like Pascal were quite popular). And even if this had been the case, I think it would have been very easy to pre-parse source code and replace long keywords by short one-byte codes (like e.g. some Basic interpreters used to do). So I find the argument "C is terse because it was invented in a period when less memory was available" a bit weak.
    – Giorgio
    Feb 2, 2013 at 14:22
7

It's pretty simple: int *p means that *p is an int; int a[5] means that a[i] is an int.

int (*f)(int (*a)[5])

Means that *f is a function, *a is an array of five integers, so f is a function taking a pointer to an array of five integers, and returning int. However, in C it isn't useful to pass a pointer to an array.

C declarations very rarely get this complicated.

Also, you can clarify using typedefs:

typedef int vec5[5];
int (*f)(vec5 *a);
4
  • 9
    Apologies if this sounds rude (I don't mean it to be), but I think you missed the entire point of the question... :\
    – user541686
    Oct 31, 2011 at 5:08
  • 2
    @Mehrdad: I can't tell you what was in Kernighan and Ritchie's mind; I did tell you the logic behind the syntax. I don't know about most people, but I don't think that your suggested syntax is clearer. Oct 31, 2011 at 6:12
  • I agree -- it's unusual to see such a complicated declaration.
    – Caleb
    Oct 31, 2011 at 19:51
  • 2
    The design of C declarations predates typedef, const, volatile, and the ability to initialize things within declarations. Many of the annoying ambiguities of declaration syntax (e.g. whether int const *p, *q; should bind const to the type or the declarand) couldn't arise in the language as originally designed. I wish the language had added a colon between the type and the declarand, but allowed its omission when using built-in "reserved-word" types without qualifiers. The the meaning of int: const *p,*q; and int const *: p,*q; would have been clear.
    – supercat
    Nov 19, 2018 at 20:45
4

I think you have to consider * [] as operators that are attached to a variable. * is written before a variable, [] after.

Let's read the type expression

int* (*p)[10];

The innermost element is p, a variable, therefore

p

means: p is a variable.

Before the variable there is a *, the * operator is always put before the expression it refers to, therefore,

(*p)

means: variable p is a pointer. Without the () the [] operator to the right would have higher precedence, i.e.

**p[]

would be parsed as

*(*(p[]))

Next step is []: since there is no further (), [] has higher precedence than the outer *, therefore

(*p)[]

means: (variable p is a pointer) to an array. Then we have the second *:

* (*p)[]

means: ((variable p is a pointer) to an array) of pointers

Finally you have the int operator (a type name), which has the lowest precedence:

int* (*p)[]

means: (((variable p is a pointer) to an array) of pointers) to integer.

So the whole system is based on type expressions with operators, and each operator has its own precedence rules. This allows to define very complex types.

0

It's not so hard when you start thinking and C never was very easy language. And int*[10]* p really isn't easier than int* (*p)[10] And what type of k would be in int*[10]* p, k;

11
  • 3
    k would be a failed code review, I can work out what the compiler will do, I may even be bothered, but I cannot work out what the programmer intended - fail............
    – mattnz
    Nov 1, 2011 at 2:25
  • 1
    because the code is unreadable and unmaintainable. Code is not correct to correct, obviously correct and likely to remain correct though maintenance. The fact you have to ask what type k will be is a sign the code fails to meat these basic requirements.
    – mattnz
    Nov 3, 2011 at 22:44
  • 1
    I have over 25 years of professional C programming behind me. I did not say I found it difficult, conversely, I find it trivial. I said it was unreadable and unmaintainable, as many programmers would break this kind of code when trying to maintain it. Defending this as acceptable coding practice would result in my workplace treating a developer as a junior, no matter how many years and how clever they were - they cannot be trusted to write reliable, maintainable and reliably maintainable code.
    – mattnz
    Nov 6, 2011 at 23:05
  • 1
    Essentially there are 3 (in this case) variable declarations of different types on the same row e.g. int *p, int i[10] and int k. That is unacceptable. Multiple declarations of the same type is acceptable, provided the variables have some form of relationship e.g. int width, height, depth; Keep in mind many people program using int* p, so whats i in 'int* p, i;'.
    – mattnz
    Nov 7, 2011 at 20:45
  • 2
    What @mattnz is trying to say is that you can be as clever as you want, but it's all meaningless when your intent isn't obvious and/or your code is poorly-written/unreadable. This kind of stuff often results in broken code and wasted time. Plus, pointer to int and int are not even the same type, so they should be declared separately. Period. Listen to the man. He has 18k rep for a reason. Aug 9, 2016 at 22:17

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.