Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the print statement change the value of pointer?

I wrote a c++ code like this:

#include <iostream>
using namespace std;

int main() {
    int i = 2;
    int i2 = 0;
    void *pi = &i - 1;
    cout << "by cout - the value of *pi is: " << *(int*)pi << endl;
    printf("by printf - the value of *pi is: %d\n", *(int*)pi);
    printf("the address of pi is: %p\n", pi);
    printf("the address of i2 is: %p\n", (void*)&i2);
    printf("the value of i2 is: %d\n", i2);
    return 0;
}

and the output is:

by cout - the value of *pi is: 0
by printf - the value of *pi is: 0
the address of pi is: 0029fe94
the address of i2 is: 0029fe94
the value of i2 is: 0

now if I remove the statement which will print the address.

#include <iostream>
using namespace std;

int main() {
    int i = 2;
    int i2 = 0;
    void *pi = &i - 1;
    cout << "by cout - the value of *pi is: " << *(int*)pi << endl;
    printf("by printf - the value of *pi is: %d\n", *(int*)pi);
    // printf("the address of pi is: %p\n", pi);
    // printf("the address of i2 is: %p\n", (void*)&i2);
    printf("the value of i2 is: %d\n", i2);
    return 0;
}

now the output is:

by cout - the value of *pi is: 2004212408
by printf - the value of *pi is: 2004212408
the value of i2 is: 0

notice that the value was totally different.

update: If I add some assignment after the print:

#include <iostream>
using namespace std;

int main() {
    int i = 2;
    int i2 = 0;
    void *pi = &i - 1;
    cout << "by cout - the value of *pi is: " << *(int*)pi << endl;
    printf("by printf - the value of *pi is: %d\n", *(int*)pi);
    // printf("the address of pi is: %p\n", pi);
    // printf("the address of i2 is: %p\n", (void*)&i2);
    pi = &i2;
    printf("the value of i2 is: %d\n", i2);
    return 0;
}

the output been normal again:

by cout - the value of *pi is: 0
by printf - the value of *pi is: 0
the value of i2 is: 0

use "g++ -std=c++11 -pedantic -Wall" to compile, version is 4.9.2.

Why could that happen?

like image 354
piratf Avatar asked Dec 15 '22 11:12

piratf


2 Answers

Accessing pi is undefined behaviour due to the fact that &i - 1 might not be a valid pointer value.

In standardese (from the C++ standard), at §5.3.1/3 we have:

[...] For purposes of pointer arithmetic (5.7) and comparison (5.9, 5.10), an object that is not an array element whose address is taken in this way is considered to belong to an array with one element of type T.

which leads us to §5.7/4:

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 pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integral expression. [...] If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

like image 155
Shoe Avatar answered Dec 26 '22 18:12

Shoe


Yes, as has been said already, your program has undefined behaviour. You just can't hack around with pointers like this and expect consistent behaviour.

Why? Because compilers are complicated creatures that do all sorts of weird magic that would ruin your expectations of memory layout.

In this case, you are just assuming that i2 will sit immediately before i in memory. This assumption is baseless. Although it happens to be the case in your working example, when your program isn't forcing i2 to occupy memory by taking its address (the &i2 in your "print statement") the variable is evidently completely optimised out. It doesn't exist in memory at all!

So no wonder trying to guess at its memory location and print the value underneath said memory location is not working as you expect.

C++ is an abstraction. It's deliberately designed with the knowledge that this sort of thing is really the choice of your compiler and cannot be predicted ahead of time. That's why C++ says that your code has undefined behaviour: because it might appear to do what you want it to do, or it might skin a cat and teleport the body into your car boot, or it might transfer all your hard-earned savings into my bank account; if you want to code to standards, just avoid undefined behaviour, assume only the guarantees of the standard, and then all this uncertainty vanishes in a poof of catnip.

like image 36
Lightness Races in Orbit Avatar answered Dec 26 '22 19:12

Lightness Races in Orbit