Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question
2  
You know there are no real answer to this, and such, questions. Right? What's you'll get are just guesses. –  BЈовић Oct 31 '11 at 7:51
4  
@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 '11 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 '11 at 19:45
    
@Caleb: Funny how you concluded that so easily, because I learned it and I still had this question... –  Mehrdad Oct 31 '11 at 22:24
1  
The cdecl command is very handy for decoding complex C declarations. There's also a web interface at cdecl.org. –  Keith Thompson Nov 1 '11 at 2:01
show 1 more comment

5 Answers

up vote 8 down vote accepted

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 ;)

share|improve this answer
1  
"making compilers easier to write"... except C is notorious for being hard to parse (only topped by C++). –  Jan Hudec Nov 1 '11 at 8:37
1  
@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 '11 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 '11 at 8:50
add comment

A lot of the oddities of the C language can be explained by the way computers worked when it was designed. There was 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.

share|improve this answer
    
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 –  Mehrdad Oct 31 '11 at 8:10
    
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 '11 at 8:33
    
The answers provided so far are pretty comprehensive: as long as they stay that way, this question is fine. –  user8 Oct 31 '11 at 8:35
2  
[Citation needed] on the source code size thing. I've never heard of such a limitation (though maybe it's something "everybody knows"). –  Sean McMillan Oct 31 '11 at 12:47
    
@Sean McMillan: The limitation certainly existed, but how heavy influence it had over C, I have no idea. I believe you have to trace the reasons all the way back to the B and BCPL languages. –  user29079 Oct 31 '11 at 13:49
show 2 more comments

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);
share|improve this answer
3  
Apologies if this sounds rude (I don't mean it to be), but I think you missed the entire point of the question... :\ –  Mehrdad Oct 31 '11 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. –  kevin cline Oct 31 '11 at 6:12
    
I agree -- it's unusual to see such a complicated declaration. –  Caleb Oct 31 '11 at 19:51
add comment

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.

share|improve this answer
add comment

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;

share|improve this answer
1  
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 '11 at 2:25
    
and why k would failed code review? –  Dainius Nov 1 '11 at 9:39
    
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 '11 at 22:44
    
if you find code like "int *p, i[10], k;" difficult, than most of C code is unmaintainable. –  Dainius Nov 4 '11 at 8:51
    
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 '11 at 23:05
show 3 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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