From past few days I was trying to learn about undefined behavior. Few days ago I found a c-faq link. This helps a lot to clear many confusions, but creates an another big confusion when I read the question #3.8. After my lots of efforts to understand the statement (specially second sentence);
The Standard states that
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.
I felt better to ask this question on SO but none of the answer there explained the second sentence of this statement. Finally, I got an explanation there about this point . After reading it and FAQ many times I concluded that;
1.The last sentence
Furthermore, the prior value shall be accessed only to determine the value to be stored
would be like this;
Furthermore, the prior value of an object shall be accessed only to determine the modified/new value( of same object ) to be stored.
As it is clear by the example
int i = 1, j, a[5];
i = i + 1;
j = i + 1;
a[i] = i;
in case of expression i = i + 1 prior value (which is 1 here) of i (in R.H.S) is accessed to determine the value of i to be stored. While in case of j = i + 1 and a[i] = i, the accessed value of i is just value not prior value as no where i is modified in these statements.
2.In case of expression a[i] = i++ or a[i++] = i, first sentence of above statement
Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression.
get failed as i is modified only once between two consecutive sequence point. And that's why we need second sentence.
Both of these examples are disallowed in C because the prior value of i accessed two times i.e, i++ itself access prior value of i in the expression to modify it and hence other access of prior value / value of i is needless as it is not accessed to determine the modified value to be stored.
The problem starts when I came up with the expression i = i++ about which it is stated in c-faq
Actually, the other expressions we've been discussing are in violation of the second sentence, as well.
I think in this expression i (in R.H.S) is accessed to determine the modified value of i.
How this expression is violating the second statement?
Think about it this way:
a = i++;
is equivalent to:
a = i;
i++;
The accesses the value of i in the increment has nothing to do with determining what value a will be stored into a. So i = i++ contains two modifications of i (which is disallowed by the first sentence), but also, the i = modification to i is independent from one of the accesses to i in i++.
I think someone was just being extra clever there. There's no need to figure how much an undefined behavior is undefined. Modifying a value twice is enough undefined.
Related
So, I understand that i++ increments post the condition is fulfilled, while ++i increments before the condition is fulfilled. That takes me to my question:
#include<stdio.h>
int main()
{
int i=0;
while(++i<10)
printf("%d\n",i);
return 0;
}
Now here we need the initialization of i from 0 as when it goes in the while loop, it will get incremented first, and thus it will be like while(1<10), and so it will print numbers from one to ten.
Second code:
#include<stdio.h>
int main()
{
int i=0;
while(i++<10)
printf("%d\n",i);
return 0;
}
Now since i++ increments after the value has been used, so why do we initialize i from i=0, as if it gets incremented after the comparision of value of i takes place, then why isn't 0 getting printed as well because the first loop should go like while(0<10), and not like while(1<10)? So, why is zero not getting printed?
Thank you for your time.
Yes, the first condition will evaluate to 0 < 10. You can verify that by changing it to while (i++ < 1) and see that the loop still runs (once), so clearly it's using 0 for the condition (1 < 1 would be false of course).
So why does it print 1? Because it doesn't print whichever value was used for the condition. It prints the current value of i. And the current value of i is 1 at that point because i was incremented right after it was used in the condition.
The difference of pre- and post- increment applies only to the value that the expression evaluates to.
After the evaluation of i++<10 is completed, the incrementation is also completed, and i has the value 1 in the first iteration.
printf("%d\n",i); is executed after the evaluation of i++<10, so i has the value 1 here in the first iteration.
Zero isn't printed because i is incremented right after the comparison, like this:
while(i<10) {
i += 1;
printf("%d\n",i);
}
The first sentence of your question is almost the answer:
i++ increments post the condition is fulfilled
It's incremented after the condition is checked, so i++ < 10 will increment i regardless of whether the condition ends up true or false.
The operation of ++ is not done before or after any “condition” it is in. It must be completed sometime in or around the full expression it is in. The answers that say i is updated “before” or “after” the ++i or i++ is evaluated are incorrect.
Prefix ++i does two separate things that may happen in any order:
It evaluates to the value of i after one is added.
It adds one to the stored value of i.
Postfix i++ does two separate things that may happen in any order:
It evaluates to the value of i before one is added.
It adds one to the stored value of i.
That change to the stored value of i is called a side effect. It is disconnected from the main evaluation of the expression. It can be performed before, during, or after the evaluation of i, but the evaluation still returns the pre- or post-increment value, as described above.
A full expression is one that is not contained inside another expression. So, in while (++i < 10), ++i < 10 is a full expression. The side effect can occur before any part of that is evaluated, after the value of i plus one is calculated, or after the < is evaluated. It can also occur in parts (such as updating the bytes of i one by one) during the evaluations. However, the side effect must occur after any previous full expression and before any later full expression. That is because the C standard says there is a sequence point between any two full expressions. (And there are some other rules about order of execution.)
Even if the stored value of i is updated before i++ or after ++i, the expression must still produce the value of i before or after the increment, respectively. For example, for i++, the compiler can fetch i, add one, store i, but then use the pre-add value in the expression.
The result of i++ is the value of i. As a side effect i is incremented.
The result of ++i is the value of i + 1. As a side effect i is incremented.
The statement
x = i++;
is logically equivalent to
tmp = i;
x = tmp;
i = i + 1;
with the caveat that the assignments to x and i can happen in any order, or even simultaneously (interleaved or in parallel).
Similarly, the statement
x = ++i;
is logically equivalent to
tmp = i + 1;
x = tmp;
i = i + 1;
with the same caveat as above. Again, these are logical equivalents, not what the compiler actually generates - depending on the compiler and the code involved there may not be a temporary.
In the second code, the increment will take place after the condition but before the printing statement.
Thus, the incremented value of i is printed.
This is your answer in simple language.
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)
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++;
About the expression statement(an example)
i = 1;
it is said that after assigning 1 to i the value of entire expression is being discarded. If the value is discarded then how this can be used later in the program,for example
printf("%d",i);
?
I know this is very basic question but I am really confused with discarded.
The value of the expression is indeed discarded, but this expression has a side effect - it changes the value of i. So next time you will access this variable, you will read the new value, which is 1.
The term "discarded" is more helpful when you do things like foo(5); or even simply "hello";. Since the expression "hello" does not have any side effect, and its value is dicarded, it is does absolutely nothing. When a compiler encounters it, as a stand alone statement:
"hello";
It may simply ignore it altogether, as if it does not exist at all. This is what happens when you call functions, or use operators:
4+5;
sin(2.6);
These expressions, too, have no side effect, and their values are ignored. When you do something like
printf("hello");
This is an expression, too. Its value is the total number of characters written. This value is ignored. But the expression must not be comletely ignored, since it has an important side effect: it prints these characters to the standard output.
So let's build a function instead of using the assignment operator (since C has no references, we'll use pointers):
int assign_int(int* var, int value) {
*var = value;
return *var;
}
now, back to your example, you do something like:
assign_int(&i, 1);
the value returned from assign_int is discarded. Just like in the printf() case. But since the function assign_int has a side effect (changing the value of i), it is not ignored by the compiler.
The important point is the i = 1 has two properties.
It changes the value stored in the variable i to be 1
It is an expression and has a value (which is also 1);
That second part is interesting is a case like
if ( (i=1) == 2 ) { // ...
or
y = 3 + (i = 1); // assign 4 to y
The line
the value of entire expression is being discarded.
refers to the value of the expression (my #2), but does not affect assignment to variable i (my #1).
What does the below code do? I'm very confused with its working. Because I thought that the if loop runs till the range of int. But I'm confused when I try to print the value of i. Please help me out with this.
#include<stdio.h>
void main()
{
static int i;
for (;;)
if (i+++”Apple”)
printf(“Banana”);
else
break;
}
It is interpreted as i++ + "Apple". Since i is static and does not have an initializer, i++ yields 0. So the whole expression is 0 + some address or equivalent to if ("Apple").
EDIT
As Jonathan Leffler correctly notes in the comments, what I said above only applies to the first iteration. After that it will keep incrementing i and will keep printing "Banana".
I think at some point, due to overflows (if it doesn't crash) "Apple" + i will yield 0 and the loop will break. Again, I don't really know what a well-meaning compiler should do when one adds a pointer and a large number.
As Eric Postpischil commented, you can only advance the pointer until it points to one-past the allocated space. In your exxample adding 7 will advance the pointer one-past the allocated space ("Apples\0"). Adding more is undefined behavior and technically strange things can happen.
Use int main(void) instead of void main().
The expression i+++"Apple" is parsed as (i++) + "Apple"; the string literal "Apple" is converted from an expression of type "6-element array of char" to "pointer to char", and its value is the address of the first element of the array. The expression i++ evaluates to the current value of i, and as a side effect, the value in i is incremented by 1.
So, we're adding the result of the integer expression i++ to the pointer value resulting from the expression "Apple"; this gives us a new pointer value that's equal or greater than the address of "Apple". So assuming the address of the string literal "Apple" is 0x80123450, then basically we're evaluating the values
0x80123450 + 0
0x80123450 + 1
0x80123450 + 2
...
all of which should evaluate to non-zero, which causes the printf statement to be executed. The question is what happens when i++ results in an integer overflow (the behavior of which is not well defined) or the value of i+++"Apple" results in an overflow for a pointer value. It's not clear that i+++"Apple" will ever result in a 0-valued expression.
This code SHOULD Have been written like this:
char *apple = "Apple";
for(i = 0; apple[i++];)
printf("Banana");
Not only is it clearer than the code posted in the original, it is also clearer to see what it does. But I guess this came from "Look how bizarre we can write things in C". There are lots of things that are possible in C that isn't a great idea.
It is also possible to learn to balance a plate of hot food on your head for the purpose of serving yourself dinner. It doesn't make it a particularly great idea - unless you don't have hands and feet, I suppose... ;)
Edit: Except this is wrong... The equivalent is:
char *apple = "Apple";
for(i = 0; apple+i++ != NULL;)
printf("Banana");
On a 64-bit machine, that will take a while. If it finishes in reasonable time (sending output to /dev/null), I will update. It takes approximitely three minutes on my machine (AMD 3.4GHz Phenom II).