5

I was looking at this page: https://en.cppreference.com/w/c/language/operator_precedence

What caught my attention was that the only description of the parenthesis operator was function call. Does this mean that the expression x = a * (b+c)-(d*e) has two function calls?

I searched in C grammar and C standard but I was unable to find anything that either supports or contradicts this.

Lundin
  • 195,001
  • 40
  • 254
  • 396
klutt
  • 30,332
  • 17
  • 55
  • 95
  • 6
    No, parentheses are used just as much to force operator precedence, exactly as is done in simple math. – Thomas Jager May 22 '19 at 13:49
  • `parenthesis operator`.. interesting, where did you find that? – Sourav Ghosh May 22 '19 at 13:51
  • It means that when `()` is used for a function call (irrelevant of however else it may be used) it has precedence 1. A bit further down you can see `(type) Type cast` with precedence 2, for instance. – Blaze May 22 '19 at 13:52
  • Looks like you missed some chapters from the standard. Ex. 6.5.1p5 – KamilCuk May 22 '19 at 13:52
  • Try searching the C grammar page for `'('`. They're used in many more places than just in postfix expressions (=> function calls). – Petr Skocik May 22 '19 at 13:52
  • 2
    `x = a * (b+c)-(d*e)` is an expression, not a statement. Here `(` is not an operator. The term "parentheses operator" refers to where `()` are used as an operator. Similarly in `int *p = 0;` the `*` and `=` are not operators, even though the same symbols are used for operators in different contexts – M.M May 22 '19 at 13:54
  • @KamilCuk So what you're saying is that primary expression and that () is NOT an operator? – klutt May 22 '19 at 13:55
  • https://port70.net/~nsz/c/c11/n1570.html#6.5.1 – StoryTeller - Unslander Monica May 22 '19 at 13:55
  • @SouravGhosh On the page I linked. – klutt May 22 '19 at 13:56
  • @Broman Re-read that one. It's function call operator, not parenthesis operator. – Sourav Ghosh May 22 '19 at 13:58
  • @SouravGhosh So regular parenthesis is not an operator? – klutt May 22 '19 at 13:59
  • @Broman Nope, they are not. – Sourav Ghosh May 22 '19 at 13:59
  • The root of the problem is that the linked precedence table is complete and utter trash. There's so much wrong with it that we should petition to cppreference to shut down that page. It cannot be salvaged. – Lundin May 22 '19 at 14:03
  • @Lundin That sounds like a good foundation for a nice answer. I made an answer once based on a rant on tutorialspoint. – klutt May 22 '19 at 14:07
  • It seems that (one of) my misunderstanding is to assume that regular parentheses are considered operators, but no answer below touches that particular issue – klutt May 22 '19 at 14:08
  • 3
    @Lundin Really? I've found it to be a good reference which help the average reader to understand operator precedence without having to grok the full language grammar from the standard. – dbush May 22 '19 at 14:09
  • @Lundin Here it is: https://stackoverflow.com/a/55731680/6699433 – klutt May 22 '19 at 14:13
  • I once started some project to provide a reliable precedence table for SO but it stranded. My main objection against the cppreference one is wrong names for almost every operator. – Lundin May 22 '19 at 14:24

3 Answers3

6

Parenthesis can be used as a function call operator, but that's not the only thing they're used for. They are also used for expression grouping as in your example.

What you're looking for is in section 6.5.1 of the C standard which discusses Primary Expressions:

Syntax

1

primary-expression:
  identifier
  constant
  string-literal
  ( expression )
  generic-selection

...

5 A parenthesized expression is a primary expression. Its type and value are identical to those of the unparenthesized expression. It is an lvalue, a function designator, or a void expression if the unparenthesized expression is, respectively, an lvalue, a function designator, or a void expression.

As stated above, parenthesis can be used to group expressions.

The use as a function call operator is detailed in section 6.5.2 on Postfix Expressions:

postfix-expression:
  ...
  postfix-expression(argument-expression-list opt)
  ...

So in your expression:

x = a * (b+c)-(d*e)

The use of parenthesis here matches a Primary Expression but not a Postfix Expression.

Also, besides expression grouping, parenthesis are used in other parts of the language grammar. Section 6.8.4 regarding Selection Statements uses parenthesis in the grammar of if and switch statements:

  • if (expression) statement
  • if (expression) statement else statement
  • switch (expression) statement

And section 6.8.5 regarding Iteration Statements also use parenthesis in the grammar of while and for statements.

  • while (expression) statement
  • do statement while (expression);
  • for (expressionopt; expressionopt; expressionopt) statement
  • for (declaration expressionopt; expressionopt ) statement
dbush
  • 205,898
  • 23
  • 218
  • 273
  • Could you add something about the fact that parenthesis is not always considered an operator. That what seems to be (one of) my misunderstandings. – klutt May 22 '19 at 14:36
4

A function call is a postfix expression.

Here in these expressuins

x = a * (b+c)-(d*e);

subexpressioins (b+c) and (d*e) are primary expressions. You may enclose any expression in parentheses and you'll get a primary expression.

For example you could even rewrite the expression statement the following way

( x = ( ( ( a ) * (b+c) )-(d*e) ) );

In this expression statement there are the following primary expressions

( a )
(b+c)
(d*e)
( ( a ) * (b+c) )
( ( ( a ) * (b+c) )-(d*e) )
( x = ( ( ( a ) * (b+c) )-(d*e) ) )

Here are some examples of postfix expressions

( *p )() // a function call

a[n] // using the subscript operator

x++; // using the postfix increment operator

The definition of a function call is

postfix-expression:
    primary-expression
    postfix-expression ( argument-expression-listopt )

From the C Standard (6.5.2.2 Function calls)

1 The expression that denotes the called function92) shall have type pointer to function returning void or returning a complete object type other than an array type.

Here are some examples of weird function calls.:)

#include <stdio.h>

void f( void )
{
    printf( "Hello " );
}

void g( void )
{
    puts( "Broman" );
}    

int main( void )
{
    void ( *funcs[] )( void ) = { f, g };

    void ( **p_func )( void ) = funcs;

    ( *p_func++ )();
    p_func[0]();
}

The program output is

Hello Broman

Take into account that in these calls

    ( *p_func++ )();
    p_func[0]();

expression ( *p_func++ ) is a primary expression and expression p_func[0] is a postfix expression (See the partial definition of the postfix expression above)

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • `(*p)()` and `(*p)` are both *postfix-expression*, maybe slightly confusing to the newbie. When talking about function calls, the term "the postfix-expression" normally refers to the expression before the function call operator. – M.M May 22 '19 at 14:15
2

No. identifier( calls identifier as a function. If there's no identifier or complete expression immediately left of the parentheses there is no call.

When I was first learning c I had the opposite problem. I couldn't figure out why clrscr; didn't clear the screen. (It's an expression that evaluates to a pointer to clrscr but does nothing with it).

In fact you can have expressions of type pointer to function, and those expressions can be called with (), and the grammar is completely unambiguous between the two. So clrscr(); is a function call, and so is (clrscr)(). On reaching function pointers, we can do resolve_function()() as well. The operation is always ( immediately after an expression, not after an operator. If it's after an operatior, it must be grouping parenthesis.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • The grammar for a function call is “*postfix-expression* `(` *argument-expression-list* `)`”, where the *argument-expression-list* is optional. The *postfix-expression* does not need to be an identifier. It can be a pointer to a function, even a function call that returns a pointer to a function. – Eric Postpischil May 22 '19 at 14:23
  • @EricPostpischil: You are correct. I took a guess at OP's level and wrote accordingly. – Joshua May 22 '19 at 15:09
  • You can edit your answer. I often face the problem of addressing a novice while also wanting to present the complete truth, and there are various approaches. You can write that “*identifer* `(` *arguments* `)`” is always a function call, and that parentheses without an expression immediately before them are a parenthesized expression or a cast (and not a function call), and then you can add later that “*postfix-expression* `(` *arguments* `)`” is also a function call, which is used with pointers to functions as well as function names. – Eric Postpischil May 22 '19 at 21:56
  • It could also be good to mention that the C grammar, which is a formal description of the structure of C source code, makes it always possible to tell them apart. – Eric Postpischil May 22 '19 at 22:00