Can we consider () unary operator when calling a function ...
No. Or rather, you can consider anything you like to be anything
else you like, but that will not make it so.
The parentheses
used in function calls are merely "syntax", not "operators", in
the jargon[%] used by compiler-writers to communicate with other
compiler-writers.
-----
[%] "Jargon", in this case, means a specialized subset of English
as used in a technical field. In law, for instance, the word
"whereas" has a special jargon-ized meaning. Medicine is full of
jargon, some of which has been popularized by TV shows: most people
know that "stat" means "right now", for instance. Here, I am going
to use the words "precedence" and "associativity", which are both
computer-science (CS) jargon, and also "parse" and "grammar", which
are CS jargon adopted from English-grammar jargon.
-----
And Unary operators like !, +, -, ~, ... are said to be having
associativity right to left ...
Unary operators do not need associativity. Some people may say
they have it, but this is usually shorthand for something else.
C's formal specification (either one, C89 or C99) has *neither*
precedence *nor* associativity. It uses a fully factored grammar
in which ambiguous parses are impossible. This is, however,
isomorphic to other grammars that *do* allow ambiguous parses, in
which the ambiguity is resolved by "precedence and associativity".
Consider the expression:
a - b * c + d
In C, this "means" the same thing as:
(a - (b * c)) + d
which is quite different from:
a - (b * (c + d))
But how are we supposed to *know* that the b*c binds the most
tightly, and then the a-(result) and (result)+d bindings should
happen?
The C standards (C89 and C99 both) make sure we come up with the
correct bindings by laborious effort. The grammar includes these
bits:
multiplicative-expr:
cast-expr
multiplicative-expr * cast-expr
multiplicative-expr / cast-expr
multiplicative-expr % cast-expr
additive-expr:
multiplicative-expr
additive-expr + multiplicative-expr
additive-expr - multiplicative-expr
...
expression:
assignment-expr
expression , assignment-expr
(and let me just claim without support that each of a, b, c, and
d are eventually allowed by "cast-expr", and that assignment-expr
eventually includes additive-expr).
To parse the entire expression as an "expression", we have to find
some way for
a - b * c + d
to be allowed by this grammar. That is, the whole thing has to
be an "expression", which can be an "assignment-expr", and an
assignment-expr can be an additive-expr.
While "a" is (through a long chain) a valid "cast-expr", "a - b"
is *not* a valid cast-expr, so it is not a multiplicative-expr
either. This means that "a - b" *cannot* be the left hand side
(LHS) of a multiplicative-expr. But note that "a" *can* be the
*entirety* of a multiplicative-expr, and an additive-expr can
consist entirely of a multiplicative-expr. Similarly, an additive-expr
can consist of a multiplicative-expr on the right-hand-side (RHS)
of the sequence "additive-expr - multiplicative-expr" (because the
LHS multiplicative-expr is allowed as an additive-expr, even though
an additive-expr is not allowed as a multiplicative-expr).
Now, b*c is of course a valid multiplicative-expr, and if we match
"a - b*c" up against the rule:
additive-expr:
additive-expr - multiplicative-expr
and make the first "a" the additive-expr and b*c the multiplicative-expr,
it all works, so this whole thing is itself an additive-expr. This
allows us to collect it all up into one bound-up group, calling
that an "additive-expr"; and now we can use that as the LHS of:
additive-expr:
additive-expr + multiplicative-expr
where "d" is the multiplicative-expr.
If you have managed to follow all that, you should see that we
have gotten this as our final "parse tree":
+
/ \
- d
/ \
a *
/ \
b c
Parsing is all about getting the right parse tree, and the C89 and
C99 standards both use a fully-factored grammar to make sure that
you can *only* get *one* parse tree for any valid expression. There
are two problems with such grammars, though:
- they are painful for people to use, compared to operator-precedence
grammars; and
- they are less efficient in automated parser-generators (like
yacc and bison) than operator-precedence grammars.
In an operator-precedence grammar, we just describe the syntax for
an "expression" giving *all* the possible operators, e.g.:
expr:
variable-name
expr + expr
expr - expr
expr * expr
expr / expr
But this means we have "ambiguous parses": a + b * c (with or without
the "- d" on the end) can be (a + b)-as-an-expr * c-as-an-expr, or
a-as-an-expr + (b * c)-as-an-expr. So we throw in a new concept
called "precedence", and say that * and / "have precedence" over +
and -, meaning you should bind b*c more tightly than a+(result), to
make sure you get the right parse tree.
Precedence fixes the "bind multiply before binding add" problem,
but does not tell you how to resolve expressions like e-f-g: is
that (e-f)-g, or is that e-(f-g)? Do you bind the left "-" first
or the right "-"? They have the same operator-precedence, so this
does not help. So now we throw in yet another concept, "associativity",
and say that "-" is left-associative so that we bind the left "-"
first, giving (e-f)-g.
Again, this is easier for *people* (and to some extent, nicer in
yacc and bison as well), but it is just a way to make sure that we
get the right parse tree. A computer *can* use a fully-factored
grammar instead, and the C standards do.
*All* of this is *just* used to build a parse tree. It does not,
I repeat DOES NOT, control actual run-time order of evaluation. To
see that "order of evaluation" is a separate issue, consider a C
source line that says:
sum = f() + g();
where functions f() and g() both print a line to stdout showing that
they have run:
int f(void) { puts("f() called"); return 17; }
int g(void) { puts("g() called"); return 25; }
When you run a program that uses f()+g(), one of the two will get
called first, then the other -- and nobody says which one will
happen first, and this is up to the compiler -- but there is no
precedence and no associativity needed to figure out how to parse
a simple "x + y" addition. Since we have "runtime order of
evaluation" with no "precedence", they *must* be separate things.
... can anyone explain what exactly does associativity means?
Here is the executive-summary version:
- The C standards do not use an operator-precedence grammar, but
*you* can, if you prefer.
- If you *do* use an operator-precedence grammar, you will need
to remember which operators "have precedence" over the others, and
use that to resolve parse ambiguities. After using precedence,
if (and ONLY if) there are still more ambiguities, you will need
to remember the "associativity" of the remaining ambiguous
operators, and use that to resolve them.
- Precedence and associativity DO NOT control run-time order of
evaluation. They only affect the compile-time parse tree. The
parse tree will usually have some sort of "partial order" effect
on runtime code, but to get *guaranteed* runtime sequencing, you
have to use C's "sequence points".