Why indexes are different from regular variables - c++

Can anyone explain me, please. Why in C++
array[i] = array[++i]; doesn't work like array[i] = array[i + 1];
but i = ++i; does work like i = i + 1;

The value evaluations of the left and right operands of the assignment operator are not sequenced.
Thus this expression
array[i] = array[++i];
can behave either like
array[i] = array[i + 1];
or like
array[i + 1] = array[i + 1];
On the other hand (C++ Standard)
...in all cases, the assignment is sequenced after the value computation
of the right and left operands,
Thus in this expression
i = ++i;
the left operand will be overwritten by the value of ++i.

The answer to your question is regarding the return values of pre-increment, no increment and post-increment.
The expression ++i increments the variable,then returns the incremented value.
The expression i++ increments the variable, the returns the value before incrementing.
So, the expressions array[i + 1] and array[++i] refer the same element, except array[++i] has the side effect of incrementing the index variable.

The problem you are having is that you have misunderstood what is happening with ++i.
++i is a statement saying increment before the evaluation of the current line and place that result in the expression. The reason ++i works like just adding one and assigning is because that is essentially what it does! These statements are equivalent:
array[i] = array[ ++i ]
array[i] = array[ i = i + 1 ]
Remember that the assignment operator returns the value from the right hand side (the new value assigned). The only reason why that's different than just saying array[i + 1] is because of the side effect that i has been changed.
Now, about i = ++i. Using the same logic we can replace that with
i = (i = i + 1)
Observe now that once you have assigned i, you return the value on the right hand side...meaning of course that that is equivalent to:
i = i + 1

Related

When should I decrement a variable inside of a bracket and when should I do it outside?

I was implementing a version of insertion sort when I noticed my function did not work properly if implemented the following way. This version is supposed to sort the elements as they are copied into a new array while keeping the original intact.
vector<int> insertionSort(vector<int>& heights) {
vector<int> expected(heights.size(), 0);
int j, key;
for(int i = 0; i < expected.size(); i++){
expected[i] = heights[i];
j = i-1;
key = expected[i];
while(j >= 0 && expected[j] > key){
expected[j+1] = expected[j--];
}
expected[j+1] = key;
}
return expected;
}
I noticed that when doing expected[j--] the function does not work as it should but when I decrement outside of the bracket it works fine.
In other words, what is the difference between
while(j >= 0 && expected[j] > key){
expected[j+1] = expected[j--];
}
and
while(j >= 0 && expected[j] > key){
expected[j+1] = expected[j];
--j;
}
To answer this, we need to take a look at what order the arguments to expected[j+1] = expected[j--]; are evaluated in. Looking at cppreference's page on order of evaluation, we see the following applies for C++17 and newer:
In every simple assignment expression E1 = E2 and every compound assignment expression E1 #= E2, every value computation and side effect of E2 is sequenced before every value computation and side effect of E1
In your case, this means that every value computation and side effect of expected[j--] is computed before it begins evaluating expected[j+1]. In particular, that means that j+1 will be based on the value j has after you've decremented it with j--, not the value it had before.
Prior to C++17, it was indeterminate whether the left hand side or the right hand side of the assignment operation was sequenced first. This means that in C++14 and earlier, your code exhibits undefined behavior:
If a side effect on a memory location is unsequenced relative to a value computation using the value of any object in the same memory location, the behavior is undefined.
In this case "a memory location" is j and the decrement in j-- is unsequenced relative to the value computation j+1. This is very similar to cppreference's example of undefined behavior:
a[i] = i++; // undefined behavior until C++17
In the second version of your code, the decrement to j does not take place until after the assignment has been completed.

strange behavior of arr[-1] in c++

#include<iostream>
using namespace std;
int main(){
int arr[] = {1,2,3};
printf("outside loop trail 1: arr[-1] = %d \n", arr[-1]);
for(int i = 0; i<10; i++){
printf("\ninside loop trail i = %d, arr[-1] = %d \n", i, arr[-1]);
}
}
Question:
Why the output inside the loop is the sequence 0, 1, 2 (same as the loop index i); but the output outside the loop changes every time I execute the code? Thanks!
Output after
g++ -o explore explore.cpp && ./explore
outside loop trail 1: arr[-1] = 537839344
inside loop trail i = 0, arr[-1] = 0
inside loop trail i = 1, arr[-1] = 1
inside loop trail i = 2, arr[-1] = 2
run ./explore for a second time:
outside loop trail 1: arr[-1] = 1214220016
inside loop trail i = 0, arr[-1] = 0
inside loop trail i = 1, arr[-1] = 1
inside loop trail i = 2, arr[-1] = 2
This is actually covered in the standard. For example, C++17 [expr.add] /4 states:
When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i + j] if 0 <= i + j <= n; otherwise, the behavior is undefined.
The reason I'm discussing adding pointers and integers is because of the equivalence of array[index] and *(array + index), as per C++17 [expr.sub] /1 (that's sub as in subscripting, not subtraction):
The expression E1[E2] is identical (by definition) to *((E1)+(E2)).
Now that's a lot to take in but it basically means that the result of adding a 'pointer to array element' and an 'index', gives you a pointer that is required to point to either an element in the array or just beyond the last one(1).
Since a pointer before the first one (array[-1]) does not meet that requirement, it's undefined behaviour. Once you do that, all bets are off and the implementation is free to do what it likes. You can count yourself lucky it didn't erase your hard disk after playing derisive_laughter.ogg :-)
Note that there's nothing wrong per se with a negative index, the following code gives you the second element (the final "pointer" is still within the array):
int array[100];
int *ptrThird = &(array[2]);
int second = ptrThird[-1];
(1) Note that a pointer is allowed to point just beyond the array provided you don't try to dereference it. Unfortunately, array[index] is a dereferncing operation so, while int array[10]; int *p = &(array[10]); is valid, int x = array[10]; is not.
This is an undefined behavior.
Generally speaking, an array index used this way is equivalent to doing pointer math thusly:
arr[n] -> *(arr+n)
By using a negative index, you are referencing memory before the start of the memory block associated with the array data. If you use an index that is outside the bounds of the array, the result is, as others have pointed out, undefined.
expr.sub/1:
A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall be a glvalue of type “array of T” or a prvalue of type “pointer to T” and the other shall be a prvalue of unscoped enumeration or integral type. The result is of type “T”. The type “T” shall be a completely-defined object type. The expression E1[E2] is identical (by definition) to *((E1)+(E2)), except that in the case of an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise. The expression E1 is sequenced before the expression E2.
expr.add/4:
When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.
(4.1) If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value.
(4.2) Otherwise, if P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i + j] if 0 ≤ i + j ≤ n and the expression P - J points to the (possibly-hypothetical) element x[i - j] if 0 ≤ i − j ≤ n.
(4.3) Otherwise, the behavior is undefined.

C++: Post-Increments resulted in the same value

The following of post-increments will result as follows:
n = 1;
j = n++; //j = 1, n = 2
j = n++; //j = 2, n = 3
j = n++; //j = 3, n = 4
My question is why the following resulted in n = 1 and not n = 3?
n = 1;
n = n++; //n = 1
n = n++; //n = 1
n = n++; //n = 1
If the code was done with pre-increment of n (++n), the result is n = 4 which is to be expected. I know the second code segment should never be done like that in the first place but it is something that I came across and I was curious as to why it resulted like that.
Please advise.
Your second example is not allowed and has an undefined behaviour.
You should use a temporary variable if you need something like that. But hardly you need something like that.
Quoting Wikipedia:
Since the increment/decrement operator modifies its operand, use of
such an operand more than once within the same expression can produce
undefined results. For example, in expressions such as x − ++x, it is
not clear in what sequence the subtraction and increment operators
should be performed. Situations like this are made even worse when
optimizations are applied by the compiler, which could result in the
order of execution of the operations to be different from what the
programmer intended.
Other examples from C++11 standard include:
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented
f(i = -1, i = -1); // the behavior is undefined
The other answers explain correctly that this code results in undefined behaviour. You may be interested as to why the behaviour is as you see it on your compiler.
As far as most compilers are concerned the expression x = n++ will be compiled into the following fundamental instructions:
take a copy of n, call it n_copy;
add 1 to n
assign n_copy to x
Therefore the expression n = n++ becomes:
take a copy of n, call it n_copy;
add 1 to n
assign n_copy to n
Which is logically equivalent to:
assign n to n
Which is logically equivalent to:
do nothing.
That's why in your case you see n == 1. Not all compilers will necessarily produce the same answer.

What is the difference between pre-increment and post-increment in the cycle (for/while)?

My interest is in the difference between for and while loops. I know that the post-increment value is used and then incremented and the operation returns a constant pre-increment.
while (true) {
//...
i++;
int j = i;
}
Here, will j contain the old i or the post-incremented i at the end of the loop?
Since the statement i++ ends at the ; in your example, it makes no difference whether you use pre- or post-increment.
The difference arises when you utilize the result:
int j = i++; // i will contain i_old + 1, j will contain the i_old.
Vs:
int j = ++i; // i and j will both contain i_old + 1.
Depends on how you use them.
i++ makes a copy, increases i, and returns the copy (old value).
++i increases i, and returns i.
In your example it is all about speed. ++i will be the faster than i++ since it doesn't make a copy.
However a compiler will probably optimize it away since you are not storing the returned value from the increment operator in your example, but this is only possible for fundamental types like a int.
Basic answer for understanding.
The incrementation operator works like this:
// ++i
function pre_increment(i) {
i += 1;
return i;
}
// i++
function post_increment(i) {
copy = i;
i += 1;
return copy;
}
A good compiler will automatically replace i++ with ++i when it detect that the returned value will not be used.
In pre-increment the initial value is first incremented and then used inside the expression.
a = ++i;
In this example suppose the value of variable i is 5. Then value of variable a will be 6 because the value of i gets modified before using it in a expression.
In post-increment value is first used in a expression and then incremented.
a = i++;
In this example suppose the value of variable i is 5. Then value of variable a will be 5 because value of i gets incremented only after assigning the value 5 to a .
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argp)
{
int x = 5;
printf("x=%d\n", ++x);
printf("x=%d\n", x++);
printf("x=%d\n", x);
return EXIT_SUCCESS;
}
Program Output:
x=6
x=6
x=7
In the first printf statement x is incremented before being passed to printf so the value 6 is output, in the second x is passed to printf (so 6 is output) and then incremented and the 3rd printf statement just shows that post increment following the previous statement by outputting x again which now has the value 7.
i++ uses i's value then increments it but ++i increments i's value before using it.
The difference between post- and pre-increment is really, in many cases subtle. post incremenet, aka num++, first creates a copy of num, returns it, and after that, increments it. Pre-increment, on the other hand, aka ++num, first evaluates, then returns the value. Most modern compilers, when seeing this in a loop, will generally optimize, mostly when post increment is used, and the returned initial value is not used. The most major difference between the 2 increments, where it is really common to make subtle bugs, is when declaring variables, with incremented values: Example below:
int num = 5;
int num2 = ++num; //Here, first num is incremented,
//then made 6, and that value is stored in num2;
Another example:
int num = 5;
int num2 = num++; //Here, num is first returned, (unfortunately?), and then
//incremented. This is useful for some cases.
The last thing here I want to say is BE CAREFUL WITH INCREMENTS. When declaring variables, make sure you use the right increment, or just write the whole thing out (num2 = num + 1, which doesn't always work, and is the equivalent of pre-increment). A lot of trouble will be saved, if you use the right increment.
it does not matter if you use pre or post increment in an independent statement, except for the pre-increment the effect is immediate
//an example will make it more clear:
int n=1;
printf("%d",n);
printf("%d",++n);// try changing it to n++(you'll get to know what's going on)
n++;
printf("%d",n);
output:
123

Array Initialization

I am trying to assign values within an array within the condition of a for loop:
#include <iostream>
using namespace std;
int* c;
int n;
int main()
{
scanf("%d", &n);
c = new int[n];
for (int i = 0; i < n; c[i] = i++ )
{
printf("%d \n", c[i]);
}
return 0;
}
However, I am not obtaining the desired output, for n = 5, 0 1 2 3 4. Instead, if I am using the instruction, c[i] = ++i, I am obtaining the output -842150451 1 2 3 4. Could you please explain me we does my code behave like this and how can I correct it?
The value of the expression ++i is the value after i has been incremented. So if it started at 0, you assign value 1 the first time and so on. You can see where the value got assigned, but asking why it got assigned there opens a can of worms.
Using i in an expression where i is modified via i++ or ++i is undefined behavior unless there is a so-called "sequence point" in between the two. In this case, there isn't. See Undefined behavior and sequence points for this rather complicated part of the language.
Although the behaviour is undefined by the standard, and may not be consistent from one run to another, clearly your program has done something. Apparently it didn't assign to index 0 at all (at least, not before the first print, which is understandable considering that the loop body happens before the last part of the "for"), so you got whatever just so happened to be in that raw memory when it was allocated to you. It assigned 1 to index 1 and so on.
This means that it may also have attempted to assign the value 5 to c[5], which is a class of bug known as a "buffer overrun", and more undefined behavior on top of what you've already got. Attempting to assign to it probably overwrites other memory, which on any given day may or may not contain something important.
The fix is to assign some value to c[0], and don't try to assign to c[5], which doesn't exist anyway, and don't try to invalidly use i "at the same time as" incrementing it. Normally you'd write this:
for (int i = 0; i < n; ++i) {
c[i] = i;
printf("%d \n", c[i];
}
If you're desperate for some reason to assign in the third clause of a for loop, you could use the comma operator to introduce a sequence point:
for (int i = 0; i < n; c[i] = i, ++i) {
}
But of course if you do that then you can't print the value of c[i] in the loop body. It hasn't been assigned yet, because the third clause isn't evaluated until the end of each loop.
You could also try c[i] = i+1, ++i, but not ++i, c[i] = i because then we're back to trying to assign to c[5], on the last iteration.
First you need to understand that the last part of the for loop is executed at the end of each iteration, so the reason you see this:
-842150451 1 2 3 4
Is because you print c[0] before it is assigned, so the value could be anything. The rest falls into line as expected.
Lesson; don't be sneaky and stuff things into the last part of the for loop like that. Make your code clear and simple:
for (int i = 0; i < n; ++i )
{
c[i] = i;
printf("%d \n", c[i]);
}
Firstly, you are claiming that you want to assign the values "within the condition of the loop". In for loop the condition is the second part of the header (i < n in your case). You are performing the assignment in the third part, which is not a condition. So, why are you saying that you want to assign the values within the condition, and yet not doing that?
Secondly, expressions like c[i] = i++ or c[i] = ++i do not have any defined behavior in C++ language. In C++ it is illegal to modify a variable and at the same time read it for any other purpose without an intervening sequence point. Yet, you are doing exactly that. There's no meaningful explanation for the behavior of your code. The behavior of your code is undefined. It can do anything for any random reason.
Thirdly, initializing anything in the for condition is generally not a good idea. Could you explain in more detail what you are trying to do and why? Without it it is hard to come up with anything meaningful.
Your fundamental problem is how the statements in the for(;;) structure get broken down and executed. The for(st1; st2; st3) structure is intended to be identical to:
st1;
while (st2) {
<body>
st3;
}
Therefore, your 3rd statement, c[i] = i++, gets executed after the printf statement and you're printing uninitialized data.
The pre-increment vs. post-increment issue is obscuring this.
The reason why c[i] = ++i produces undefined behavior. It's undefined to both ++ a value (pre or post) and use it again within the same expression. In this case it appears that ++i is being evaluated before anything else and causing the execution to essentially be
c[1] = 1;
c[2] = 2;
...
This means c[0] is never initialized and instead has essentially a garbage value. It seems like the order you want is
c[0] = 0;
c[1] = 1;
To get this ordering you'll need to separate the initialization and increment into separate statements.
c[i] = i;
i++;