After reading this answer about undefined behavior and sequence points, I wrote a small program:
#include <stdio.h>
int main(void) {
int i = 5;
i = (i, ++i, 1) + 1;
printf("%d\n", i);
return 0;
}
The output is 2. Oh God, I didn't see the decrement coming! What is happening here?
Also, while compiling the above code, I got a warning saying:
px.c:5:8: warning: left-hand operand of comma expression has no effect
[-Wunused-value] i = (i, ++i, 1) + 1;
^
Why? But probably it will be automatically answered by the answer of my first question.
In the expression (i, ++i, 1), the comma used is the comma operator
the comma operator (represented by the token ,) is a binary operator that evaluates its first operand and discards the result, and then evaluates the second operand and returns this value (and type).
Because it discards its first operand, it is generally only useful where the first operand has desirable side effects. If the side effect to the first operand does not takes place, then the compiler may generate warning about the expression with no effect.
So, in the above expression, the leftmost i will be evaluated and its value will be discarded. Then ++i will be evaluated and will increment i by 1 and again the value of the expression ++i will be discarded, but the side effect to i is permanent. Then 1 will be evaluated and the value of the expression will be 1.
It is equivalent to
i; // Evaluate i and discard its value. This has no effect.
++i; // Evaluate i and increment it by 1 and discard the value of expression ++i
i = 1 + 1;
Note that the above expression is perfectly valid and does not invoke undefined behavior because there is a sequence point between the evaluation of the left and right operands of the comma operator.
Quoting from C11, chapter 6.5.17, Comma operator
The left operand of a comma operator is evaluated as a void expression; there is a
sequence point between its evaluation and that of the right operand. Then the right
operand is evaluated; the result has its type and value.
So, in your case,
(i, ++i, 1)
is evaluated as
i, gets evaluated as a void expression, value discarded
++i, gets evaluated as a void expression, value discarded
finally, 1, value returned.
So, the final statement looks like
i = 1 + 1;
and i gets to 2. I guess this answers both of your questions,
How i gets a value 2?
Why there is a warning message?
Note: FWIW, as there is a sequence point present after the evaluation of the left hand operand, an expression like (i, ++i, 1) won't invoke UB, as one may generally think by mistake.
i = (i, ++i, 1) + 1;
Let's analyse it step by step.
(i, // is evaluated but ignored, there are other expressions after comma
++i, // i is updated but the resulting value is ignored too
1) // this value is finally used
+ 1 // 1 is added to the previous value 1
So we obtain 2. And the final assignment now:
i = 2;
Whatever was in i before it's overwritten now.
The outcome of
(i, ++i, 1)
is
1
For
(i,++i,1)
the evaluation happens such that the , operator discards the evaluated value and will retain just the right most value which is 1
So
i = 1 + 1 = 2
You'll find some good reading on the wiki page for the Comma operator.
Basically, it
... evaluates its first operand and discards the result, and then evaluates the second operand and returns this value (and type).
This means that
(i, i++, 1)
will, in turn, evaluate i, discard the result, evaluate i++, discard the result, and then evaluate and return 1.
You need to know what the comma operator is doing here:
Your expression:
(i, ++i, 1)
The first expression, i, is evaluated, the second expression, ++i, is evaluated, and the third expression, 1, is returned for the whole expression.
So the result is: i = 1 + 1.
For your bonus question, as you see, the first expression i has no effect at all, so the compiler complains.
Comma has an 'inverse' precedence. This is what you will get from old books and C manuals from IBM (70s/80s). So the last 'command' is what is used in parent expression.
In modern C its use is strange but is very interesting in old C (ANSI):
do {
/* bla bla bla, consider conditional flow with several continue's */
} while ( prepAnything(), doSomethingElse(), logic_operation);
While all operations (functions) are called from left to right, only the last expression will be used as a result to conditional 'while'.
This prevent handling of 'goto's to keep a unique block of commands to run before condition check.
EDIT: This avoid also a call to a handling function which could take care of all logic at left operands and so return the logical result. Remember that, we had not inline function in the past of C. So, this could avoid a call overhead.
Related
As per C, PostIncrement(a++) > Dereference(*) > Assignment(=)
When I execute this below c snippet,
#include <stdio.h>
int main(){
int arr[]= {1,2,3};
int *p = a;
*p++ = 3;
for(int i=0;i<3;i++){
printf("%d ",arr[i]);
}
}
Output:
3 2 3
But if we apply order of precedence in this statement,
*p++ = 3;
The statement will be evaluated in the following order:
p++ will be evaluated
*p will get dereferenced.
then 3 will be assigned to *p using the assignment operator
If we apply the above order,
p which is pointing to the start of the array arr, will get incremented first and point to the second element of the array. Then second element's address will get dereferenced and then 3 will be assigned to the second index. So our expected output should be 1 3 3
But the output I got is 3 2 3.
I know that my expected output is not correct. It'll be helpful if you explain the order of evaluation here in this case of the output of the compiler.
Precedence only determines the grouping of operators with operands - it does not control the order in which expressions are evaluated. Precedence rules only determine that
*p++ = 3;
should parsed as
*(p++) = 3; // as opposed to (*p)++ = 3 or *(p++ = 3)
not that operations be executed in a specific order.
The ++ and –– operators have a result and a side effect. The result of postfix ++ is the current value of the operand; the side effect is to increment the operand. Your expression is logically equivalent to
tmp = p;
*tmp = 3;
p = p + 1;
with the caveat that the assignment to *tmp and the update of ptr can happen in any order, and they can even be interleaved or executed in parallel.
The result of a post-increment expression is the value of the operand before it is incremented. Thus, even though the ++ in *p++ does, indeed, have higher precedence than the *, the latter is applied to the result of the p++ expression which is, as just mentioned, the initial value of p.
According to the C Standard (6.5.2.4 Postfix increment and decrement operators)
2 The result of the postfix ++ operator is the value of the operand.
As a side effect, the value of the operand object is incremented (that
is, the value 1 of the appropriate type is added to it). See the
discussions of additive operators and compound assignment for
information on constraints, types, and conversions and the effects of
operations on pointers. The value computation of the result is
sequenced before the side effect of updating the stored value of the
operand. With respect to an indeterminately-sequenced function call,
the operation of postfix ++ is a single evaluation. Postfix ++ on an
object with atomic type is a read-modify-write operation with
memory_order_seq_cst memory order semantics.
Thus in this statement
*p++ = 3;
the expression p++ returns the current value of the pointer p that is a pointer that points to the first element of the array. It is this pointer to the first element of the array that is dereferenced.
So this statement
*p++ = 3;
is not equivalent to the statements
p++;
*p = 3;
"The statement will be evaluated in the following order:
p++ will be evaluated
*p will get dereferenced.
then 3 will be assigned to *p using the assignment operator"
Post Increments operator doesn't work like that. You can assume like this for better understanding.
When p++ gets evaluated, first, its present value is stored at a temporary location then the increment takes places.
All remaining operations are performed on the value stored in temporary location.
For subsequent statements incremented value is used.
I just wonder if, for the following code, the compiler uses associativity/precedence alone or some other logic to evaluate.
int i = 0, k = 0;
i = k++;
If we evaluate based on associativity and precedence, postfix ++ has higher precedence than =, so k++(which becomes 1) is evaluated first and then comes =, now the value of k which is 1 is assigned to i.
So the value of i and k would be 1. However, the value of i is 0 and k is 1.
So I think that the compiler splits this i = k++; into two (i = k; k++;). So here compiler is not going for the statements associativity/precedence, it splits the line as well. Can someone explain how the compiler resolves these kinds of statements?
++ does two separate things.
k++ does two things:
It has the value of k before any increment is performed.
It increments k.
These are separate:
Producing the value of k occurs as part of the main evaluation of i = k++;.
Incrementing k is a side effect. It is not part of the main evaluation. The program may increment the value of k after evaluating the rest of the expression or during it. It may even increment the value before the rest of the expression, as long as it “remembers” the pre-increment value to use for the expression.
Precedence and associativity are not involved.
This effectively has nothing to do with precedence or associativity. The increment part of a ++ operator is always separate from the main evaluation of an expression. The value used for k++ is always the value of k before the increment regardless of what other operators are present.
Supplement
It is important to understand that the increment part of ++ is detached from the main evaluation and is sort of “floating around” in time–it is not anchored to a certain spot in the code, and you do not control when it occurs. This is important because if there is another use or modification of the operand, such as in k * k++, the increment can occur before, during, or after the main evaluation of the other occurrence. When this happens, the C standard does not define the behavior of the program.
Postfix operators have higher precedence than assignment operators.
This expression with the assignment operator
i = k++
contains two operands.
It is equivalently can be rewritten like
i = ( k++ );
The value of the expression k++ is 0. So the variable i will get the value 0.
The operands of the assignment operator can be evaluated in any order.
According to the C Standard (6.5.2.4 Postfix increment and decrement operators)
2 The result of the postfix ++ operator is the value of the operand.
As a side effect, the value of the operand object is incremented (that
is, the value 1 of the appropriate type is added to it).
And (6.5.16 Assignment operators)
3 An assignment operator stores a value in the object designated by
the left operand. An assignment expression has the value of the left
operand after the assignment,111) but is not an lvalue. The type of an
assignment expression is the type the left operand would have after
lvalue conversion. The side effect of updating the stored value of
the left operand is sequenced after the value computations of the left
and right operands. The evaluations of the operands are unsequenced.
Unlike C++, C does not have "pass by reference". Only "pass by value". I'm going to borrow some C++ to explain. Let's implement the functionality of ++ for both postfix and prefix as regular functions:
// Same as ++x
int inc_prefix(int &x) { // & is for pass by reference
x += 1;
return x;
}
// Same as x++
int inc_postfix(int &x) {
int tmp = x;
x += 1;
return tmp;
}
So your code is now equivalent to:
i = inc_postfix(k);
EDIT:
It's not completely equivalent for more complex things. Function calls introduces sequence points for instance. But the above is enough to explain what happens for OP.
It's similar to (only with an additional sequence point for illustration):
i = k; // i = 0
k = k + 1; // k = 1
Operator associativity doesn't apply here. Operator precedence merely states which operand that sticks to which operator. It's not particularly relevant in this case, it just says that the expression should be parsed as i = (k++); and not as (i = k)++; which wouldn't make any sense.
From there on, how this expression is evaluated/executed is specified by specific rules for each operator. The postfix operator is specified to behave as (6.5.2.4):
The value computation of the result is sequenced before the side effect of
updating the stored value of the operand.
That is, k++ is guaranteed to evaluate to 0 and then at some point later on, k is increased by 1. We don't really know when, only that it happens somewhere between the point when k++ is evaluated but before the next sequence point, in this case the ; at the end of the line.
The assignment operator behaves as (6.5.16):
The side effect of updating the stored value of the left operand is
sequenced after the value computations of the left and right operands.
In this case, the right operand of = has its value computed before updating the left operand.
In practice, this means that the executable can look as either this:
k is evaluated to 0
set i to 0
increase k by 1
semicolon/sequence point
Or this:
k is evaluated to 0
increase k by 1
set i to 0
semicolon/sequence point
Precedence and associativity only affect how operators and operands are associated with each other - they do not affect the order in which expressions are evaluated. Precedence rules dictate that
i = k++
is parsed as
i = (k++)
instead of something like
(i = k)++
The postfix ++ operator has a result and a side effect. In the expression
i = k++
the result of k++ is the current value of k, which gets assigned to i. The side effect is to increment k.
It's logically equivalent to writing
tmp = k
i = tmp
k = k + 1
with the caveat that the assignment to i and the update to k can happen in any order - the operations can even be interleaved with each other. What matters is that i gets the value of k before the increment and that k gets incremented, not necessarily the order in which those operations occur.
The fundamental issue here is that precedence is not the right way to think about what
i = k=+;
means.
Let's talk about what k++ actually means. The definition of k++ is that if gives you the old value of k, and then adds 1 to the stored value of k. (Or, stated another way, it takes the old value of k, plus 1, and stores it back into k, while giving you the old value of k.)
As far as the rest of the expression is concerned, the important thing is what the value of k++ is. So when you say
i = k++;
the answer to the question of "What gets stored in i?" is, "The old value of k".
When we answer the question of "What gets stored in i?", we don't think about precedence at all. We think about the meaning of the postfix ++ operator.
See also this older question.
Postscript: The other thing you have to be really careful about is when you think about the side question, "When does it store the new value into k? It turns out that's a really hard question to answer, because the answer is not as well defined as you might like. The new value gets stored back into k sometime before the end of the larger expression it's in (formally, "before the next sequence point"), but we don't know whether it happens before or after, say, the point at which the thing gets stored into i, or before or after other interesting points in the expression.
Ahh, this is quite an interesting question. To help you understand better, this is what actually happens.
I'm going to try to explain using a bit of operator overloading concepts from C++, so bear with me if you do not know C++.
This is how you would overload the postfix-increment operator:
int operator++(int) // Note that the 'int' parameter is just a C++ way of saying that this is the postfix and not prefix operator
{
int copy = *this; // *this just means the current object which is calling the function
*this += 1;
return copy;
}
Essentially what the postfix-increment operator does is that it creates a copy of the operand, increases the original variable, and then returns the copy.
In your case of i = k++, k++ does actually happen first but the value returned is actually k (think of it like a function call). This then gets assigned to i.
I have a simple C code and big confusion about expressions containing comma(,) operator(s).
int main(){
int i=0,j=11,c;
c=i=j,++i;
printf("c=%d i=%d\n",c,i);
c=(i=j,++i);
printf("c=%d i=%d\n",c,i);
return 0;
}
The above code prints:
c=11 i=12
c=12 i=12
My questions are:
What is the actual work of comma(,) as an operator?
++ has more precedence than , and =, why evaluation is done for the expression on left of comma?
What will be the order if an expression contains operators with different priority, will it depend on comma(,)?
Is it behaving like a substitute of semicolon(;)?
The assignment operator has a higher priority then the comma operator.
Thus expression
c = i = j, ++i;
is equivalent to
( c = i = j ), ++i;
According to the C Standard (6.5.17 Comma operator)
2 The left operand of a comma operator is evaluated as a void
expression; there is a sequence point between its evaluation and that
of the right operand. Then the right operand is evaluated; the result
has its type and value.114)
In the expression above the result of the comma operator is discarded but it has a side effect of increasing i.
In this expression
c = ( i = j, ++i );
due to using parentheses you changed the order of the evaluation of the above expression. Now it is equivalent to
c = ( ( i = j ), ++i );
and variable c gets the value of expression ++i according to the quote from the C Standard listed above.
operator comma is to execute many statement and return only result of last statement.
So for c=i=j,++i; : c=i=j is executed, then ++i and after that result of ++i is returned (but not used).
And for c=(i=j,++i);, according to operator precedence, i=j is executed, and just after ++i is executed, and then affectation to c of result of (i=j, ++i), which is the result of last statement, i.e. ++i
So, the behavior of comma is not really same as semicolon. You can use it as a substitute like in c=i=j,++i;.
Personally, I do not encourage to use this operator, which generates less readable and less maintainable code
What is the actual work of comma(,) as an operator?
The comma operator is mainly a superfluous feature. See this for a description of how it works.
++ has more precedence than , and =, why evaluation is done for the expression on left of comma?
What will be the order if an expression contains operators with different priority, will it depend on comma(,)?
The left operand is evaluated for side effects. The result of the comma operator is the result of the evaluated right operand. Note that the comma operator has the lowest precedence of all operators in C.
Is it behaving like a substitute of semicolon(;)?
Kind of, yeah. Both a semi-colon and the comma operator includes a sequence point. The difference is that the comma operator isn't the end of a statement, so it can be squeezed in with other operators on the same line, and it also returns a result.
There is really no reason why you ever would want to do this though. The main use of the comma operator is to obfuscate code, it should be avoided. The only reason why you need to learn how it works, is because you might encounter crap code containing it.
For example, your nonsense code should be rewritten into something more readable and safe:
int main(){
int i=0;
int j=11;
int c;
i=j;
c=j;
i++;
printf("c=%d i=%d\n",c,i);
i=j;
i++;
c=i;
printf("c=%d i=%d\n",c,i);
return 0;
}
Well let's split it. In the first case c and i take the value of j => c=i=j=11; then you increment i => i=12; So the code is equivalent to this
c = j;
i = j;
++i;
For the second case i takes the value of j => i=j=11 and then you increment i => i=12 and then c takes the value of i => c = 12;
So the code is equivalent to this:
i = j;
++i;
c = i;
The comma operator will evaluate and discard all operations, up to, but not including, the final operation. This allows any number of non-consequential operations to be invoked together on a single line where only the last operation is of interest.
Think of it this way, if you have a number of loop variables to increment at any one location in a loop, you can separate all the additions/subtraction, etc.. over their own individual variables on separate line, but why? Where they are executed (within reason) is of no-consequence to the operation of the code. They then can be invoked on a single line with no adverse effect to the code.
According to sequence point definition, sequence points are "specified points in the execution sequence called sequence points, all side effects of previous evaluations are guaranteed to be complete"
So in the below program, all side effects of ++ operator must have been performed before going to second part of && operator, i.e, i should be incremented to 1 as && is a sequence point.
#include<stdio.h>
int main()
{
int i=0,a;
a=i++&&1;
printf("%d",a);
getchar();
return 0;
}
Expected output:
1 (1&&1=1)
actual output :
0
Why doesn't i increment before 2nd part?
Using the ternary operator also gives same output:
#include<stdio.h>
int main()
{
int i=0,a;
a=(i++)?1:0;
printf("%d",a);
getchar();
return 0;
}
The ternary operator is also a sequence point. So shouldn't this give output 1 instead of 0?
i++
Evaluates to previous value of i.
As a side effect value of i is incremented by 1.
So yes the sequence point is there but the expression i++ evaluates to 0 (though value of i is 1 at the same time)
For the expected results use ++i instead of i++.
From 6.5.2.4 Postfix increment and decrement operators in C11 specs:
The result of the postfix ++ operator is the value of the operand. As
a side effect, the value of the operand object is incremented (that
is, the value 1 of the appropriate type is added to it). See the
discussions of additive operators and compound assignment for
information on constraints, types, and conversions and the effects of
operations on pointers. The value computation of the result is
sequenced before the side effect of updating the stored value of the
operand. With respect to an indeterminately-sequenced function call,
the operation of postfix ++ is a single evaluation. Postfix ++ on an
object with atomic type is a read-modify-write operation with
memory_order_seq_cst memory order semantics.98)
98) Where a pointer to an atomic object can be formed and E has
integer type, E++ is equivalent to the following code sequence where T
is the type of E:
T *addr = &E;
T old = *addr;
T new;
do {
new = old + 1;
} while (!atomic_compare_exchange_strong(addr, &old, new));
with old being the result of the operation. Special care must be taken
if E has floating type; see 6.5.16.2.)
Will the right side of the expression get evaluated first or the left ?
void main ()
{
int i = 0 , a[3] ;
a[i] = i++;
printf ("%d",a[i]) ;
}
The order of evaluation of the operands of the assignment operator is unspecified: the operands may be evaluated in any order.
However, this expression (a[i] = i++) yields undefined behavior because you both modify i (using i++) and you separately read i (using a[i]) without a sequence point in between those actions.
C does not define which side gets evaluated first. The standard states (C99 §6.5/2):
Between the previous and next sequence
point an object shall have its stored
value modified at most once by the
evaluation of an expression.
Furthermore, the prior value shall be
accessed only to determine the value
to be stored
The aforementioned result you posted is thereby UB.