Is writing 3 instructions separated by comma `,` undefined behaviour? - c

I think that I saw somewhere that writing more than 1 instruction separated by a comma , is undefined behavior.
So does the following code generate undefined behavior?
for (i=0, j=3, k=1; i<3 && j<9 && k<5; i++, j++, k++) {
printf("%d %d %d\n", i, j, k);
}
because there are 3 instructions separated by a comma , :
i++, j++, k++

writing more than 1 instruction separated by comma , is undefined behaviour.
Nope, it's not the general case.
In your case, i++, j++, k++ is perfectly valid.
FWIW, as per C11, chapter §6.5.17, Comma operator (emphasis mine)
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; [...]
[Note]: You might have got confused by seeing something along the line of
printf("%d %d %d", i++, ++i, i);
kind of statement, but do note, there the , is not a comma operator altogether (rather, a separator for supplied arguments) and the sequencing does not happen. So, those kind of statements are UB.
Again, referring to the standard, footnote 3 for the same chapter
As indicated by the syntax, the comma operator (as described in this subclause) cannot appear in contexts where a comma is used to separate items in a list (such as arguments to functions or lists of initializers).

Your example is perfectly fine C code.
There are instances where the comma has a different meaning, for example in declaration statements. In declaration statements, comma is used to separate the declaration of several variables.
int a;
a = 1,2,3; // Ok. a is assigned the value 3.
int a = 1,2,3; // Not ok!
int a = 1, b = 2; // Ok! a is assigned the value 1.

Related

Why This Code Prints Second statment?

Why This code fail in first If statement?
My prediction getting wrong as per associations and precedence.
#include<stdio.h>
void main()
{
int i=10;
if(i==i--)
{
printf("In 1:%d\n",i);
printf("TRUE 1\n");
}
i=10;
if(i==--i)
{
printf("In 2:%d\n",i);
printf("TRUE 2\n");
}
}
i==i-- is undefined behaviour. Please check this: http://c-faq.com/expr/ieqiplusplus.html and this: http://c-faq.com/expr/seqpoints.html
The expression i==i-- will cause undefined behavior because there is no sequence point between the two evaluations of i and i--. This means that anything can happen and at that point the program no longer produces meaningful output.
The same is true for the expression i==--i
If an object is read and also modified without a sequence point separating the two events the behavior is undefined1. In this case the same object is modified (side effect): i-- and read (value computation): i, without a sequence point.
Correct code would separate the two expressions with a sequence point (character ;):
const int i1 = i;
const int i2 = i--;
if( i1 == i2 )
{
//...
}
const int i3 = i;
const int i4 = --i;
if( i3 == i4 )
{
//...
}
1 (Quoted from ISO/IEC 9899:201x 6.5 Expressions 2):
If a side effect on a scalar object is unsequenced relative to either a different side effect
on the same scalar object or a value computation using the value of the same scalar
object, the behavior is undefined.
Undefined behaviour because post and pre increment depend on compiler. Please see stack overflow question here.
C99 standard - 6.5 Expressions, §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 read only to determine the value
to be stored.
There would be no compilation error. It does not go inside the first if statement as it is undefined behavior (as user babon mentioned).
The behavior actually depends on the compiler you're using.
I did not quite get your question. Are you asking "why does statements in only first if gets executed"? If yes, following is the reason
i-- evaluates the expression and then decrements
--i decrements first and then evaluate expression. So, second condition evaluates to 'false' (10 == 9)

left to right evaluation of statements inside of a for loop separated by comma

Are these both codes same? These statements inside of for loop are written in same line separated by comma. Will they be evaluated for left to right?
Also i wanted to ask can i use as many as statements inside of for loop separated by comma. Like for(i=0, j=0, k=0; .......) ?
for(i=0, j= strlen(s)-1; i<j; i++, j--){
c=s[i];
s[i]=s[j];
s[j]=c;
}
and
for(i=0, j= strlen(s)-1; i<j; i++, j--)
c=s[i],s[i]=s[j],s[j]=c;
The C comma operator evaluates each of the two operands, discarding the result of the first and returning the second. With more than one comma, the operator is left associative so the effect is an evaluation from left to right.
Your second example will thus do the same thing as your first example. However it is poor style because there is no reason to use the comma operator, unlike i=0, j-strlen(s)-1 within the body of the for statement, where a semicolon could not have been used.
, operator is evaluated left to right and there is a sequence point between the evaluation of the left and the right operand, so it means both codes are equivalent.

Please explain the output of following C code

Code :
#include<stdio.h>
#include<stdlib.h>
int arr[] = {1, 2, 3, 4};
static int count = 0;
int incr( ) {
++count;
++count;
return count;
}
int main(void)
{
printf("\ncount= %d \n",count);
int i;
arr[count++]=incr( );
for(i=0;i<4;i++)
printf("arr[%d]=%d\n", i,arr[i]);
printf("\nIncremented count= %d \n",count);
return 0;
}
Output
count = 0
arr[0]=2
arr[1]=2
arr[2]=3
arr[3]=4
Incremented count = 1
The final incremented value of global variable count is 1 even though it has been incremented thrice.
When count++ is replaced by count in arr[count++]=incr( ) the final incremented value of count is 2.
This is undefined behaviour from bad sequencing. On this line:
arr[count++]=incr( );
What happens (with your compiler) is:
arr[count] is resolved to arr[0], postfix ++ will be applied at the end of the
statement;
incr() is called, count is now equal to 2, incr() returns 2;
arr[0] gets assigned 2;
postfix ++'s side effect kicks in, and count is now equal to 1. Previous changes to count are lost.
You will find more info on "side effects" and "sequence points" by googling their real name :)
To understand why your code goes wrong, you must first understand undefined behavior and sequence points, which is a rather advanced topic. You also need to understand what undefined behavior is, and what unspecified behavior is, explained here.
If you do something to a variable which counts as a side-effect, such as modifying it, then you are not allowed to access that variable again before the next sequence point, for other purposes than to calculate which value to store in your variable.
For example i = i++ is undefined behavior because there are two side effects on the same variable with no sequence point in between. But i = i+1; is well-defined, because there is only one side effect (the assignment) and the i+1 is only a read access to determine what value to store.
In your case, there is no sequence point between the arr[count++] sub-expression and the incr() sub-expression, so you get undefined behavior.
This is how sequence points appear in functions, C11 6.5.2.2:
There is a sequence point after the evaluations of the function
designator and the actual arguments but before the actual call. Every
evaluation in the calling function (including other function calls)
that is not otherwise specifically sequenced before or after the
execution of the body of the called function is indeterminately
sequenced with respect to the execution of the called function.
This means that the contents of the function aren't sequenced in relation to the rest of the expression. So you are essentially writing an expression identical to arr[count++] = ++count;, except through the function you managed to squeeze in two unsequenced ++count on the right side of the operation, which wouldn't otherwise be possible. Any any rate, it is undefined behavior.
Fix your code by enforcing sequence points between the left hand and the right hand of the expression. However, the order of evaluation of sub-expressions is unspecified behavior, so you need to ensure that your code is safe no matter if the left or right side is evaluated first. This code will fix the problems:
// artificial example, don't write code like this
0,arr[count++] = 0,incr();
since the comma operator introduces a sequence point. But of course, writing nonsense code like that isn't something you should be doing. The real solution is to never use ++ together with other operators in the same expression.
// good code, write code like this
arr[count] = incr();
count++;

Sequence points in C / function calls

I'm just learning some C, or rather, getting a sense of some of the arcane details. And I was using VTC advanced C programming in which I found that the sequence points are :
Semicolon
Comma
Logical OR / AND
Ternary operator
Function calls (Any expression used as an argument to a function call is finalized the call is made)
are all these correct ?. Regarding the last one I tried:
void foo (int bar) { printf("I received %d\n", bar); }
int main(void)
{
int i = 0;
foo(i++);
return 0;
}
And it didnt print 1, which according to what the VTC's guy said and if I undertood correctly, it should, right ?. Also, are these parens in the function call the same as the grouping parens ? (I mean, their precedence). Maybe it is because parens have higher precedence than ++ but I've also tried foo((i++)); and got the same result. Only doing foo(i = i + 1); yielded 1.
Thank you in advance. Please consider that I'm from South America so if I wasnt clear or nothing makes sense please, oh please, tell me.
Warmest regards,
Sebastian.
Your code is working like it should and it has nothing to do with sequence points. The point is that the postfix ++ operator returns the non-incremented value (but increments the variable by 1 nonetheless).
Basically:
i++ – Increment i by one and return the previous value
++i – Increment i by one and return the value after the increment
The position of the operator gives a slight hint for its semantics.
Sequence means i++ is evaluted before foo is invoked.
Consider this case (I am not printing bar!):
int i = 0;
void foo (int bar) { printf("i = %d\n", i); }
int main(void){
foo(i++);
return 0;
}
i = 1 must be printed.
C implements pass-by-value semantics. First i ++ is evaluated, and the value is kept, then i is modified (this may happen any time between the evaluation and the next sequence point), then the function is entered with the backup value as the argument.
The value passed into a function is always the same as the one you would see if using the argument expression in any other way. Other behavior would be fairly surprising, and make it difficult to refactor common subexpressions into functions.
When you do something like:
int i = 0, j;
j = i++;
the value of i is used first and then incremented. hence in your case the values of i which is 0 is used (hence passed to your function foo) and then incremented. the incremented values of i (now 1) will be available only for main as it is its local variable.
If you want to print 1 the do call foo this way:
foo(++i);
this will print 1. Rest you know, why!

Multiple conditions in a C 'for' loop

I came across this piece of code. I generally use '&&' or '||' to separate multiple conditions in a for loop, but this code uses commas to do that.
Surprisingly, if I change the order of the conditions the output varies.
#include<stdio.h>
int main() {
int i, j=2;
for(i=0; j>=0,i<=5; i++)
{
printf("%d ", i+j);
j--;
}
return 0;
}
Output = 2 2 2 2 2 2
#include<stdio.h>
int main(){
int i, j=2;
for(i=0; i<=5,j>=0; i++)
{
printf("%d ", i+j);
j--;
}
return 0;
}
Output = 2 2 2
Can somebody explain the reason? It seems to be checking only the last comma-separated condition.
The comma operator evaluates all its operands and yields the value of the last one. So basically whichever condition you write first, it will be disregarded, and the second one will be significant only.
for (i = 0; j >= 0, i <= 5; i++)
is thus equivalent with
for (i = 0; i <= 5; i++)
which may or may not be what the author of the code intended, depending on his intents - I hope this is not production code, because if the programmer having written this wanted to express an AND relation between the conditions, then this is incorrect and the && operator should have been used instead.
Of course it is right what you say at the beginning, and C logical operator && and || are what you usually use to "connect" conditions (expressions that can be evaluated as true or false); the comma operator is not a logical operator and its use in that example makes no sense, as explained by other users. You can use it e.g. to "concatenate" statements in the for itself: you can initialize and update j altogether with i; or use the comma operator in other ways
#include <stdio.h>
int main(void) // as std wants
{
int i, j;
// init both i and j; condition, we suppose && is the "original"
// intention; update i and j
for(i=0, j=2; j>=0 && i<=5; i++, j--)
{
printf("%d ", i+j);
}
return 0;
}
The comma expression takes on the value of the last (eg. right-most) expression.
So in your first loop, the only controlling expression is i<=5; and j>=0 is ignored.
In the second loop, j>=0 controls the loop, and i<=5 is ignored.
As for a reason... there is no reason. This code is just wrong. The first part of the comma-expressions does nothing except confuse programmers. If a serious programmer wrote this, they should be ashamed of themselves and have their keyboard revoked.
Do not use this code; whoever wrote it clearly has a fundamental misunderstanding of the language and is not trustworthy. The expression:
j >= 0, i <= 5
evaluates "j >= 0", then throws it away and does nothing with it. Then it evaluates "i <= 5" and uses that, and only that, as the condition for ending the loop. The comma operator can be used meaningfully in a loop condition when the left operand has side effects; you'll often see things like:
for (i = 0, j = 0; i < 10; ++i, ++j) . . .
in which the comma is used to sneak in extra initialization and increment statements. But the code shown is not doing that, or anything else meaningful.
Wikipedia tells what comma operator does:
"In the C and C++ programming languages, 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)."
There is an operator in C called the comma operator. It executes each expression in order and returns the value of the last statement. It's also a sequence point, meaning each expression is guaranteed to execute in completely and in order before the next expression in the series executes, similar to && or ||.
Completing Mr. Crocker's answer, be careful about ++ or -- operators or I don't know maybe other operators.
They can affect the loop. for example I saw a code similar to this one in a course:
for(int i=0; i++*i<-1, i<3; printf(" %d", i));
The result would be $ 1 2$. So the first statement has affected the loop while the outcome of the following is lots of zeros.
for(int i=0; i<3; printf(" %d", i));

Resources