int i=1,2,3,4; // Compile error
// The value of i is 1
int i = (1,2,3,4,5);
// The value of i is 5
What is the difference between these definitions of i in C and how do they work?
Edit: The first one is a compiler error. How does the second work?
= takes precedence over ,1. So the first statement is a declaration and initialisation of i:
int i = 1;
… followed by lots of comma-separated expressions that do nothing.
The second code, on the other hand, consists of one declaration followed by one initialisation expression (the parentheses take precedence so the respective precedence of , and = are no longer relevant).
Then again, that’s purely academic since the first code isn’t valid, neither in C nor in C++. I don’t know which compiler you’re using that it accepts this code. Mine (rightly) complains
error: expected unqualified-id before numeric constant
1 Precedence rules in C++ apply regardless of how an operator is used. = and , in the code of OP do not refer to operator= or operator,. Nevertheless, they are operators as far as C++ is concerned (§2.13 of the standard), and the precedence of the tokens = and , does not depend on their usage – it so happens that , always has a lower precedence than =, regardless of semantics.
You have run into an interesting edge case of the comma operator (,).
Basically, it takes the result of the previous statement and discards it, replacing it with the next statement.
The problem with the first line of code is operator precedence. Because the = operator has greater precedence than the , operator, you get the result of the first statement in the comma chain (1).
Correction (thanks #jrok!) - the first line of code neither compiles, nor is it using the comma as an operator, but instead as an expression separator, which allows you to define multiple variable names of the same type at a time.
In the second one, all of the first values are discarded and you are given the final result in the chain of items (5).
Not sure about C++, but at least for C the first one is invalid syntax so you can't really talk about a declaration since it doesn't compile. The second one is just the comma operator misused, with the result 5.
So, bluntly, the difference is that the first isn't C while the second is.
Related
I came across the following when learning C++
int a = 5;
-----a;
The second statement doesn't compile. The statement could either be read as --(--(-a)) or -(--(--a)), since both operators are in the same precedence group. In this case though only the second interpretation (when you DO use brackets) makes sense. Therefore I see no ambiguity.
My question therefore is: why is the unary negation not in a higher precedence group than the prefix decrement?
I am reading "C++ Primer (5th Edition)" and I've run in something I'm not sure I understand correctly.
The example is pretty much similar to one they gave in the book. Imagine we have some function that returns string (or any class that has non-static members):
string some_function(par1, par2) {
string str;
// some code
return str;
}
I know that you can use the return value of any function to access its members, i.e. something like this is valid:
auto size = some_function(arg1, arg2).size(); // or whatever member of class
However, since the dot operator . and function call operator () have left to right grouping and same precedence, the above expression should be something like this:
(some_function(arg1, arg2)).size()
I suppose I am right so far? The thing I don't understand here is order of evaluation. Since order of evaluation is not specified for . operator, it means that either some_function(arg1, arg2) or size() will be evaluated first. But how can it evaluate size() first if it doesn't know on which object is it working on? This implies that order of evaluation should be fixed from left to right, but it is not. How is this possible?
Another example is something like this:
cin.get().get();
Again, it seems like first cin.get() should be evaluated before second get() since it won't know on which object is it working, but this doesn't seem to be necessarily the case.
Operators of the same precedence are evaluated according to their associativity, which you correctly observe is left-to-right for the operator group containing the function call and element selection operators. Therefore, yes, given the expression
x = foo().bar();
The order of operations is
x = (((foo()).bar)());
accounting for relative precedence and associativity of all operators involved. No one writes code in that manner, though.
Likewise, given
cin.get().get()
the order of operations is
(((cin.get)()).get)()
, so yes, the precedence rules result in the cin.get() sub-expression being evaluated first, yielding the object to which the second . (and thence the rest of the expression) is applied.
I am working in C++ (not C++/CLI) in Visual Studio 2012.
I don't understand why this code works, I would have expected it to fail at compilation time, but it doesn't even fail at runtime:
double MyClass::MyMethod() const
{
//some code here
return (10, 20, 30, 40);
}
I produced this code by mistake, wasn't on purpose, I noticed the mistake when I was running my Unit Tests. And I am surprised it works. When I run it, it returns 40, the last number on the list.
Can someone explain me what this syntax means and why it works?
This is using the comma operator which will evaluate each expression from left to right but only return the last. If we look at the draft C++ standard section 5.18 Comma operator it says:
A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded value expression (Clause 5).83 Every value computation and side effect associated with the left expression is sequenced before every value computation and side effect associated with the right expression.
the linked article gives the most common use as:
allow multiple assignment statements without using a block statement, primarily in the initialization and the increment expressions of a for loop.
and this previous thread Uses of C comma operator has some really interesting examples of how people use the comma operator if you are really curious.
Enabling warning which is always a good idea may have helped you out here, in gcc using -Wall I see the following warning:
warning: left operand of comma operator has no effect [-Wunused-value]
return (10, 20, 30, 40);
^
and then two more of those.
The comma operator is a 'sequence point' in C++, often used to initialise multiple variables in for loops.
So the code is evaluating a series of integers, one at a time, as single expressions. The last of these is the returned value, and the return statement as a whole is equivalent to simply return (40);
the expression (10, 20, 30, 40) is actually a series of 4 expressions separated by , You can use , to separate multiple expressions and the result is the evaluation of the last one.
You have used the , i.e. comma operator
return () is valid.
and so is return (/*valid evaluation*/)
Comma operator returns the last value i.e 40
Let me present a example :
a = ++a;
The above statement is said to have undefined behaviors ( I already read the article on UB on SO)
but according precedence rule operator prefix ++ has higher precedence than assignment operator =
so a should be incremented first then assigned back to a. so every evaluation is known, so why it is UB ?
The important thing to understand here is that operators can produce values and can also have side effects.
For example ++a produces (evaluates to) a + 1, but it also has the side effect of incrementing a. The same goes for a = 5 (evaluates to 5, also sets the value of a to 5).
So what you have here is two side effects which change the value of a, both happening between sequence points (the visible semicolon and the end of the previous statement).
It does not matter that due to operator precedence the order in which the two operators are evaluated is well-defined, because the order in which their side effects are processed is still undefined.
Hence the UB.
Precedence is a consequence of the grammar rules for parsing expressions. The fact that ++ has higher precedence than = only means that ++ binds to its operand "tighter" than =. In fact, in your example, there is only one way to parse the expression because of the order in which the operators appear. In an example such as a = b++ the grammar rules or precedence guarantee that this means the same as a = (b++) and not (a = b)++.
Precedence has very little to do with the order of evaluation of expression or the order in which the side-effects of expressions are applied. (Obviously, if an operator operates on another expression according to the grammar rules - or precedence - then the value of that expression has to be calculated before the operator can be applied but most independent sub-expressions can be calculated in any order and side-effects also processed in any order.)
why it is UB ?
Because it is an attempt to change the variable a two times before one sequence point:
++a
operator=
Sequence point evaluation #6: At the end of an initializer; for example, after the evaluation of 5 in the declaration int a = 5;. from Wikipedia.
You're trying to change the same variable, a, twice. ++a changes it, and assignment (=) changes it. But the sequence point isn't complete until the end of the assignment. So, while it makes complete sense to us - it's not guaranteed by the standard to give the right behavior as the standard says not to change something more than once in a sequence point (to put it simply).
It's kind of subtle, but it could be interpreted as one of the following (and the compiler doesn't know which:
a=(a+1);a++;
a++;a=a;
This is because of some ambiguity in the grammar.
I have read this question and I want to add to it that what are the things that can not be done using the comma operator. This has confused me a lot, as I can do this:
int arr[3];
arr[0]=1,arr[1]=2,arr[2]=3;
But when I do:
int arr[3],arr[0]=1,arr[1]=2,arr[2]=3;
It gives me a compiler error.
I want to ask that what are the limitations of the comma operator in real practice?
One thing to realize is that not all uses of a comma in C are instances of the comma operator. Changing your second example to be a syntactically declaration:
int a0=1,a1=2,a2=3;
the commas are not operators, they're just syntax required to separate instances of declarators in a list.
Also, the comma used in parameter/argument lists is not the comma operator.
In my opinion the use of the comma operator is almost always a bad idea - it just causes needless confusion. In most cases, what's done using a comma operator can be more clearly done using separate statements.
Two exceptions that come to mind easily are inside the control clauses of a for statement, and in macros that absolutely need to cram more than one 'thing' into a single expression, and even this should only be done when there's no other reasonable option).
You can use the comma operator most anywhere that an expression can appear. There are a few exceptions; notably, you cannot use the comma operator in a constant expression.
You also have to be careful when using the comma operator where the comma is also used as a separator, for example, when calling functions you must use parentheses to group the comma expression:
void f(int, bool);
f(42, 32, true); // wrong
f((42, 32), true); // right (if such a thing can be considered "right")
Your example is a declaration:
int arr[3],arr[0]=1,arr[1]=2,arr[2]=3;
In a declaration, you can declare multiple things by separating them with the comma, so here too the comma is used as a separator. Also, you can't just tack on an expression to the end of a declaration like this. (Note that you can get the desired result by using int arr[3] = { 1, 2, 3 };).