Is it constant folding at work

A

Anshul Sawant

Have a look at the following code:

#include<iostream>

using namespace std;

int main()
{
const int a = 1;
const int* const aptr1 = &a;
int* const aptr2 = const_cast<int*>aptr1);
*aptr2 = 2;
cout<<a<<endl;
cout<<*(&a)<<endl;
cout<<*aptr1<<endl;
cout<<*aptr2<<endl;
}

Output is (for g++ (ver. 3.2) and visual C++ 6 compiler)

1
1
2
2

Is it the correct behaviour?
Is it due to constant folding?
If yes, how is constant folding possible when we are referencing the variable?
 
R

Rob Williscroft

Anshul Sawant wrote in
Have a look at the following code:

#include<iostream>

using namespace std;

int main()
{
const int a = 1;
const int* const aptr1 = &a;

This cast if fine and legal (ok you missed a '('), but ...
int* const aptr2 = const_cast<int*>aptr1);

The use of the result const_cast here is UB (uderfined Behaviour),
you should only use const_cast when you *know* the original thing
pointed to wasn't a constant.
*aptr2 = 2;

All bets are now Officialy (according to The C++ Standard) off.
cout<<a<<endl;
cout<<*(&a)<<endl;
cout<<*aptr1<<endl;
cout<<*aptr2<<endl;
}

Output is (for g++ (ver. 3.2) and visual C++ 6 compiler)

1
1
2
2

Is it the correct behaviour?

Yup, but so is you computer growing leggs and wandering off to
make a cup of tea.
Is it due to constant folding?

Its due to underfined behaviour.
If yes, how is constant folding possible when we are referencing the
variable?

HTH.

Rob.
 
J

Joost Kraaijeveld

int* const aptr2 = const_cast<int*>aptr1);
A int* const aptr2 doe not mean that the object at which aptr2 points is
const but it means that the pointer itself cannot be rebound to another
object. The object itself can be changed throught that pointer. So this line
*aptr2 = 2;

is allowed to thange the object. I do not think that the compiler can catch
that.

From a previous post:
constness for pointers
// not const at all
A. TestClass * ptr;

// non-const pointer to a const object
B. const TestClass * ptr_to_const;

// const pointer to non-const object
C. TestClass * const const_ptr;

// const pointer to const object
D. const TestClass * const const_ptr_to_const;
 
N

Nick Hounsome

Anshul Sawant said:
Have a look at the following code:

#include<iostream>

using namespace std;

int main()
{
const int a = 1;
const int* const aptr1 = &a;

To a decent compiler this is just as constant as the first one.
int* const aptr2 = const_cast<int*>aptr1);
*aptr2 = 2;

This is undefined behaviour - it could easily case a core dump on some
architectures.
cout<<a<<endl;

I would expect every compiler to print 1 for this.
cout<<*(&a)<<endl;

I am surprised that they were good enough to optimize this away.
cout<<*aptr1<<endl;
cout<<*aptr2<<endl;
}

Output is (for g++ (ver. 3.2) and visual C++ 6 compiler)

1
1
2
2

Is it the correct behaviour?

There is no correct behaviour defined.
Is it due to constant folding?

No it's due to optimization.
If yes, how is constant folding possible when we are referencing the
variable?

Be careful about taking the address of constants -
If you put a constant in a header and take its address in 2 different source
files the addresses will be different!
constant have local scope by default.
If you ever need to do this you must use extern.
 
X

Xiaobin Yang

Have a look at the following code:

#include<iostream>

using namespace std;

int main()
{
const int a = 1;
const int* const aptr1 = &a;
int* const aptr2 = const_cast<int*>aptr1);
*aptr2 = 2;
cout<<a<<endl;
cout<<*(&a)<<endl;
cout<<*aptr1<<endl;
cout<<*aptr2<<endl;
}

Output is (for g++ (ver. 3.2) and visual C++ 6 compiler)

1
1
2
2
If ( aptr1 == &a ), how is it possible that " *aptr != *(&a)" ?
confusing....
 
R

Robert Bauck Hamar

If ( aptr1 == &a ), how is it possible that " *aptr != *(&a)" ?
confusing....

a is declared as a const int. This means to the compiler that the value
of a can never change. Knowing that, the compiler just sends the value
1 to cout when it finds a or *(&a).

Exchanging the cout << X << endl whith printf("%d\n", X), g++ 3.3.3
emits:
movl $1, 4(%esp)
movl $.LC0, (%esp)
call printf
for the first calls to printf. Which says: Put the value 1 on the
stack; put the value .LC0 (.LC0 is a pointer to "%d\n") on the stack;
call printf.

const int a = 1;
generates
movl $1, -4(%ebp)
put the value 1 in memory located by -4(%ebp)

const int* const aptr1 = &a;
generates
leal -4(%ebp), %eax
load the effective address of -4(%ebp) (== &a) into %eax
movl %eax, -8(%ebp)
move the contents of %eax into memory -8(%ebp) (==aptr1)

int* const aptr2 = const_cast<int*>(aptr1);
generates
movl -8(%ebp), %eax
move the value of -8(%ebp) (==aptr1) int %eax
movl %eax, -12(%ebp)
move the value of %eax into memory -12(%ebp) (==aptr2)

*aptr2 = 2
generates
movl -12(%ebp), %eax
move the value of -12(%ebp) (==aptr2) into %eax
movl $2, (%eax)
put the value 2 into the address %eax points to (==*aptr2)

Now the value of the object a is 2, but the compiler nevertheless uses
the value 1 when it finds a in the source code. Lesson: casting away
constness doesn't always work.
 
X

Xiaobin Yang

Thanks! I see, it's the compiler that consistently replace "a" with 1.
In this sense, "const" is a little like MACRO.

a is declared as a const int. This means to the compiler that the value
of a can never change. Knowing that, the compiler just sends the value
1 to cout when it finds a or *(&a).

Exchanging the cout << X << endl whith printf("%d\n", X), g++ 3.3.3
emits:
movl $1, 4(%esp)
movl $.LC0, (%esp)
call printf
for the first calls to printf. Which says: Put the value 1 on the
stack; put the value .LC0 (.LC0 is a pointer to "%d\n") on the stack;
call printf.

const int a = 1;
generates
movl $1, -4(%ebp)
put the value 1 in memory located by -4(%ebp)

const int* const aptr1 = &a;
generates
leal -4(%ebp), %eax
load the effective address of -4(%ebp) (== &a) into %eax
movl %eax, -8(%ebp)
move the contents of %eax into memory -8(%ebp) (==aptr1)

int* const aptr2 = const_cast<int*>(aptr1);
generates
movl -8(%ebp), %eax
move the value of -8(%ebp) (==aptr1) int %eax
movl %eax, -12(%ebp)
move the value of %eax into memory -12(%ebp) (==aptr2)

*aptr2 = 2
generates
movl -12(%ebp), %eax
move the value of -12(%ebp) (==aptr2) into %eax
movl $2, (%eax)
put the value 2 into the address %eax points to (==*aptr2)

Now the value of the object a is 2, but the compiler nevertheless uses
the value 1 when it finds a in the source code. Lesson: casting away
constness doesn't always work.

-- xiaobin
 
K

Karl Heinz Buchegger

Xiaobin said:
Thanks! I see, it's the compiler that consistently replace "a" with 1.

No. It *may* do this. It doesn't need to.

The conclusio is this:
Don't lie to your compiler!

If you tell it: "Hey buddy, this is constant. 'a' has a value
of 1 and I will never change that to anything else" you should
not attempt to do exactly that: Change it to anything else.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,170
Messages
2,570,925
Members
47,464
Latest member
Bobbylenly

Latest Threads

Top