5

I have just finished reading through the language reference ( appendix A) of the 2nd edition of K&R's "The C Programming language" for a new job.

Please note, that I have worked out how to read C declarations by all the guidance here on SO ( so thanks ;) ). The explanation has always been a reference to the precedence of expressions and operators in C.
I can infer the same precedence table from the Reference Manual a.k.a. Appendix A, §A7 in said book, no problem.

However, §A7 deals with the precedence of expression operators, as is said at the beginning of the section - the square brackets postfix-expression [ expression ] are herein viewed as the subscript operator, the asterisk * is viewed as the indirection operator and parentheses ( argument-expression-list) or () pertain to function calls, see §A7.3.
So why do so many people refer to this precedence table, when the subject is C declarations, and even get a couple of upvotes on SO? The only precedence it claims to define is that of certain expression operators.

How declarations are to be parsed is written in §A8, especially §A8.6 goes into the basics of more complicated declarations.
But throughout the whole of Appendix A, there are no statements about in what precedence exactly parentheses, square brackets, asterisks, type names and so on are to be parsed.
On page 216 it says, that parentheses may change the binding of complex declarators, but now how ( although I have a hunch, see below in my example).

Let me give you an example in 'pseudo-code' where I am was at a loss, using §A8.8 ( the T's stand for the type specifiers, the D's for the declarators):

Reading a C declaration:

Original declaration:
char (*(*f())[])()

/* Have to use A8.6.3, because of how the decl. looks: */
Now let T=char , D=(*(*f())[])()

D=D1(), where D1=(*(*f())[])
The type of f, according to A8.6.3 is then  
:L1 /* label 1 */
f is "type-modifiers of f in D1" function returning char /* note, in the book it says 'type-modifiers of f in T D1, but this wouldn't make any sense! */

  Now look at T D1 = char (*(*f())[])

    :ALT_A
    The type of f in D1 = (*(*f())[]) is the same as that of f in 
    D2=*(*f())[] 
    /* At least, this is how parentheses are supposed to be understood, according to the beginning of A8.6, 
    where it says: 
      In a declaration T D where D has the form 
          ( D1 )
    then the type of the identifier in the declaration D1 is the same as that of D. */
    /* note about that quote: the identifier in D has no type, did the authors mean to imply 'incomplete' types?
    So, using incomplete types:*/
    Looking at D2 = T1 D3[] ----> have to use A8.6.2,  where T1 = * , D3 = (*f()),
    so the type of f in T1 D3 is 
    f is "type-modifiers of f in D3" array of pointers ()

      look at f in D3 = (*f()), or equivalently, f in D4 = *f(). Have to use A8.6.3. ---> 
      f is a function returning a pointer to ()  

    going up a level: f is "type-modifiers of f in D3" array of pointers () 
    translates to: f is a function returning a pointer to an array of pointers ()

going up another level: f is "type-modifiers of f in D1" function returning char 
translates to: f is a function returning a pointer to an array of pointers to functions returning a char

This is the result, cdecl also shows. Note that I haven't used any precedence table, just the reference manual and the sections directly pertaining declarations.

So how and why do so many people refer to operator precedence?
It seems to me like every reference to operator precedence when people ask about C declarations is a wrong answer giving a "parsing algorithm" that magically turns out to give the right result.

And secondly, how come that K&R seem to be so inexact with so many things ( see my /* */-remarks in the pseudo-code)? I would have expected more precision, I mean they obviously knew all the details and had to be able to think precise.

Sorry for the messed up formatting, btw.. I have spent most of this day trying to write down how I manually parse this and at the same time understanding what K&R might have meant with this and that formulation...

List of sources, where declarations and operator precedence are said to be connected:

http://users.ece.utexas.edu/~ryerraballi/CPrimer/CDeclPrimer.html ("All one has to understand any complex C declaration then, is to know that these declarations are based on the C operator precedence chart, the same one you use to evaluate expressions in C:")

http://binglongx.com/2009/01/25/how-to-read-a-cc-declaration/

How are (complex) declarations parsed in terms of precedence and associativity? ("In fact, the designers of C were wise enough to make declarations use the same "precedence rules" as expressions. This is the "declaration follows usage" rule. For example, in the expression", see first answer by Brian .)

Operator precedence in C Definitions

Expert C Programming: Deep C Secrets, p. 74, 28 5 star reviews on Google Books?

Community
  • 1
  • 1
polynomial_donut
  • 303
  • 1
  • 4
  • 13
  • "Everyone"? Who is that "everyone"? – Eugene Sh. Aug 18 '15 at 19:23
  • I adjusted that question, see above. Thanks :) – polynomial_donut Aug 18 '15 at 19:26
  • 1
    It is extremely unclear how `declaration` has anything to do with `operator precedence`. These are totally different and unrelated terms. Are you sure you're using the correct terminology here? Your statement at the beginning of the question **"I have worked out how to read C declarations... The explanation has always been a reference to the precedence of expressions and operators in C"** makes very little sense!!! – barak manos Aug 18 '15 at 19:27
  • 3
    Lets do some logical induction: If a company bothers to ask you about complicated C-type declarations, then they probably consider them important for their code base. Then, you should RUN RUN RUN and not apply for that job as obviously the code base is horrible. – BitTickler Aug 18 '15 at 19:30
  • @barak manos No it does not make very little sense, although it may be misleading. There is a sentence stating "I have worked sth out.", followed by another that states "There has been an explanation". But this does not imply, that I have worked out sth using that specific explanation. – polynomial_donut Aug 18 '15 at 19:30
  • @barakmanos The OP is pointing out the fact that Appendix A7 is dedicated to operators grammar definition, while "many people" refer to it as a source of the precedence information. I will leave to OP to prove this statement. – Eugene Sh. Aug 18 '15 at 19:31
  • @EugeneSh.: So what has that got to do with `declaration`? As far as I know, **declaration** is just a "shortcut" for symbol (functions and variables) declaration. Do we have an "overload" for this term in C? – barak manos Aug 18 '15 at 19:32
  • @BitTickler hahahah :D No they did not ask me that. I was simply asked to read through the grammar section. But who knows... oO – polynomial_donut Aug 18 '15 at 19:33
  • Operator precedence is important if you want to read badly written expressions such as ``x ^ 0x3 * 4 || 1 << 5 * 3``. (Missing brackets to avoid reader jumps out of window). Not so much in the context of type declarations, imho. – BitTickler Aug 18 '15 at 19:34
  • @polynomial_donut K&R is kinda... outdated. If the company is asking to read it... well. It is outdated as well. – Eugene Sh. Aug 18 '15 at 19:35
  • @barakmanos I am just trying to translate it :) – Eugene Sh. Aug 18 '15 at 19:36
  • @EugeneSh: So I'm not the only one here to whom it makes very little sense then. – barak manos Aug 18 '15 at 19:37
  • I have been programming professionally since 1980, and I have **never** seen `char (*(*f())[])()`. I *can* tell what it does, but we shouldn't bother. Try something else! – Bo Persson Aug 18 '15 at 19:39
  • @BoPersson: So I've got a question for you: What could `((void(*)())0)();` be used for? (I've actually used it a little while ago, although there are probably more readable ways to do it). – barak manos Aug 18 '15 at 19:43
  • @barakmanos */me rises a hand.* May I, may I? :) – Eugene Sh. Aug 18 '15 at 19:44
  • @EugeneSh: Go ahead my young apprentice. – barak manos Aug 18 '15 at 19:44
  • @barakmanos Well, yeah, it's a soft reset. I have seen such a code before, although a while ago. Which architecture it is? As it won't be valid on ARMs for example. Or any system with memory virtualization. – Eugene Sh. Aug 18 '15 at 19:46
  • @barakmanos I've given you an explanation in a comment above. Also consider the fact that I see it just the same way as you do ( How do people come up with that connection?), hence my question here -.- – polynomial_donut Aug 18 '15 at 19:46
  • @EugeneSh.: The force is strong with you. I've used it on Freescale's MC1323 (HCS08 core), but it's a platform-dependent syntax (i.e., should work on any architecture). – barak manos Aug 18 '15 at 19:48
  • @barakmanos Cortex-M are storing the initial stack pointer at address 0, not the reset vector, so it can't work. – Eugene Sh. Aug 18 '15 at 19:49
  • @EugeneSh. I have added links to back up my statement. Those were all from the first page of a google query. A glance at subsequent pages shows other 'sources' and I'm pretty sure, more would pop up on later pages... if there's a book called "Expert C Programming" that refers to precedence in connection to this, maybe somewhere K&R or K or R have stated such a rule?... It seems kind of like a huge coincidence that using operator precedence actually seems to be working out for reading C declarations. – polynomial_donut Aug 18 '15 at 19:50
  • @EugeneSh.: Of course, with virtual memory it wouldn't jump to physical address 0, but will most likely cause the process to throw a runtime exception... But I guess that's up to the OS you're using, not the underlying HW architecture. – barak manos Aug 18 '15 at 19:50
  • @barakmanos I hope you are using it for bare metal :) Otherwise it is memory violation. Anyway, looks like it's offtopic here. – Eugene Sh. Aug 18 '15 at 19:52
  • @BoPersson I'm just getting more into the language, trying to understand the semantics, that's all... I think things such as this are good exercise. – polynomial_donut Aug 18 '15 at 19:53
  • @polynomial_donut - It's exercise all right, but you don't learn anything you will ever use in a real program. I think real programs are more interesting than ["You can never guess what this code does!"](http://www.ioccc.org/) – Bo Persson Aug 18 '15 at 19:58
  • @BoPersson Oh I know about that contest :) I don't even disagree with you. But I think this is just how I learn to understand and use concepts. And I'd say it has so far helped me quite a lot - in mathematics, and in the little C I have learned. What would you suggest to me as a rather quick but hard/ good practice to up my understanding of C? – polynomial_donut Aug 21 '15 at 16:14

1 Answers1

0

It's because declaration reflects use.

In the grammar for declarations, a declarator is separated into a pointer part followed by a direct-declarator, wherein the array-forming [] and function-forming () constructs are applied, so forming an array or function type has higher precedence than forming a pointer type.

And, reflecting this, in the grammar for expressions, the postfix operators [] and () have higher precedence than the prefix unary operator *.

This means that when we see int *f() we know that this is a function returning pointer to int, because *f() gives a value of type int; likewise we know that char (*g)() is a pointer to a function returning char, because (*g)() must give a value of type char.

This is why errors were allowed to creep into Appendix A; no-one is expected to read it, because applying the declaration-reflects-use rule gives the correct result and is a lot easier to apply.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Hey - I go into the sales business for Write-Only-Memory and advertise here! Then, finally people will know where to write "Appendices no-one is expected to read" to. – BitTickler Aug 19 '15 at 00:22
  • In the grammar for declarations, a declarator is separated into a pointer part followed by a direct-declarator, wherein the array-forming [] and function-forming () constructs are applied, so forming an array or function type has higher precedence than forming a pointer type. The only statement saying that the order in which the grammar is stated would define precedence is in §7...
    – polynomial_donut Aug 21 '15 at 16:09
  • @polynomial_donut that's just an aid to the reader; every grammar has an associated set of precedence rules, so there's no need to state that the grammar for declarations implies its own precedence. – ecatmur Aug 21 '15 at 16:34
  • @ecatmur Yeah I was thinking about that as well, but then why did K&R make the effort to implicitly give another ( albeit equivalent) set of rules to interpret declarations ( see the rules I applied...)? – polynomial_donut Aug 21 '15 at 16:57
  • @polynomial_donut what's the other set of rules? All I'm aware of is the grammar and the semantics in the grammar. – ecatmur Aug 21 '15 at 17:13
  • @ecatmur see A8.6.1, A8.6.2, A8.6.3 – polynomial_donut Aug 21 '15 at 18:02
  • @polynomial_donut those are the semantics of the grammar; they aren't separate from it. – ecatmur Aug 21 '15 at 18:31