tags:

views:

1337

answers:

11

I had an article, but i lost it, it showed and described a couple of c/c++ tricks that ppl should be careful. One of them interested me but now that i am trying to replicate it I'm not being able to put it to compile.

The concept was that it is possible to change by accident the value of a const in c/c++

It was something like this:

const int a = 3; // I promisse i won't change a
const int *ptr_to_a = &a; // I still promiss i won't change a;
int *ptr;
ptr = ptr_to_a;

(*ptr) = 5; // I'm a liar, a is now 5

I wanted to show this to a friend but now I'm missing a step, does anyone know what's missing for it to start compiling and working?

ATM I'm getting invalid conversion from 'const int*' to 'int*' but when i read the article i tried and it worked great.

+4  A: 

Did you try this?

ptr = const_cast<int *>(ptr_to_a);

That should help it compile but it's not really by accident due to the cast.

Kristo
Yeah that i know but i think it was without that though
fmsf
Looks like you're gonna get the right answer, but i'll way a bit longer in case a solution without const_cast shows up. As i really believe it does
fmsf
this is incorrect.
sfossen
in that is doesn't cause the behavior requested.
sfossen
A: 

You probably want to use const_cast:

int *ptr = const_cast<int*>(ptr_to_a);

I'm not 100% certain this will work though, I'm a bit rusty at C/C++ :-)

Some readup for const_cast: http://msdn.microsoft.com/en-us/library/bz6at95h(VS.80).aspx

+4  A: 

you need to cast away the constness:

linux ~ $ cat constTest.c
#include <stdio.h>


void modA( int *x )
{
        *x = 7;
}


int main( void )
{

        const int a = 3; // I promisse i won't change a
        int *ptr;
        ptr = (int*)( &a );

        printf( "A=%d\n", a );
        *ptr = 5; // I'm a liar, a is now 5
        printf( "A=%d\n", a );

        *((int*)(&a)) = 6;
        printf( "A=%d\n", a );

        modA( (int*)( &a ));
        printf( "A=%d\n", a );

        return 0;
}
linux ~ $ gcc constTest.c -o constTest
linux ~ $ ./constTest
A=3
A=5
A=6
A=7
linux ~ $ g++ constTest.c -o constTest
linux ~ $ ./constTest
A=3
A=3
A=3
A=3

also the common answer doesn't work in g++ 4.1.2

linux ~ $ cat constTest2.cpp
#include <iostream>
using namespace std;
int main( void )
{
        const int a = 3; // I promisse i won't change a
        int *ptr;
        ptr = const_cast<int*>( &a );

        cout << "A=" << a << endl;
        *ptr = 5; // I'm a liar, a is now 5
        cout << "A=" << a << endl;

        return 0;
}
linux ~ $ g++ constTest2.cpp -o constTest2
linux ~ $ ./constTest2
A=3
A=3
linux ~ $

btw.. this is never recommended... I found that g++ doesn't allow this to happen.. so that may be the issue you are experiencing.

sfossen
tried but it doesn't work :\ the a will keep the same value and (*ptr) will have another value
fmsf
version after your edit also doesn't work, a=3, (*ptr) = 5
fmsf
using g++ on mac btw
fmsf
int 4.1.2 it's blocked in g++ but not gcc.
sfossen
it can be that the compiler caches the value of a. make a a "const volatile a = 3;", and "ptr" a "volatile int *ptr;"
Johannes Schaub - litb
The fact that it doesn't work reliably should be a good indication of why telling lying to the compiler is bad.
Ryan Graham
... or it just optimizes the stuff out, because the compiler says to itself "hmm, the standard says this produces undefined behavior. so i will just do my best and optimize this out so i'm faster".
Johannes Schaub - litb
true, but he is trying to reproduce this to demonstrate.. so gcc is the answer he needs..
sfossen
A: 
const int foo = 42;
const int *pfoo = &foo;
const void *t = pfoo;
void *s = &t; // pointer to pointer to int
int **z = (int **)s; // pointer to int
**z = 0;
dirkgently
doesn't work, after compile and run foo is still 42
fmsf
you are not compiling w/ optimizations on, are you?
dirkgently
It ran fine on my end though with VS2005 :( Will have to wait before I can lay my hands on a gcc.
dirkgently
A: 

The step you're missing is that you don't need the int* pointer. The line:

const int *ptr_to_a = &a; // I still promiss i won't change a;

actually says you won't change ptr_to_a, not a. So if you changed your code to read like this:

const int a = 3; // I promise I won't change a
const int *ptr_to_a = &a; // I promise I won't change ptr_to_a, not a.

(*ptr_to_a) = 5; // a is now 5

a is now 5. You can change a through ptr_to_a without any warning.

EDIT:

The above is incorrect. It turns out I was confusing a similar trick with a shared_ptr, in which you can get access to the raw pointer and modify the internal data value without firing off any warnings. That is:

#include <iostream>
#include <boost/shared_ptr.hpp>

int main()
{
    const boost::shared_ptr<int>* a = new boost::shared_ptr<int>(new int(3));
    *(a->get()) = 5;
    std::cout << "A is: " << *(a->get()) << std::endl;

    return 0;
}

Will produce 5.

jasedit
that doesn't compile for sure "error: assignment of read-only location"
fmsf
No, ptr_to_a is a pointer to const int, meaning exactly that you promise not to change a.
Kristo
Yeah - I know I've run into this trick before, but it's some variation on this. I'm trying to dig through code and figure out the variant I've seen.
jasedit
+2  A: 

Back in the mists of time, we paleo-programmers used FORTRAN. FORTRAN passed all its parameters by reference, and didn't do any typechecking. This meant it was quite easy to accidentally change the value of even a literal constant. You could pass "3" to a SUBROUTINE, and it would come back changed, and so every time from then on where your code had a "3", it would actually act like a different value. Let me tell you, those were hard bugs to find and fix.

Paul Tomblin
+1  A: 

Note any attempt to cast away constness is undefined by the standard. From 7.1.5.1 of the standard:

Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.

And right after this example is used:

const int* ciq = new const int (3);     //  initialized as required
int* iq = const_cast<int*>(ciq);        //  cast required
*iq = 4;                                //  undefined: modifies a  const  object

So in short what you want to do isn't possible using standard C++.

Further when the compiler encounters a declaration like

const int a = 3; // I promisse i won't change a

it is free to replace any occurance of 'a' with 3 (effectively doing the same thing as #define a 3)

Andrew Khosravian
+8  A: 

Just a guess, but a common question is why one can't convert an int** to a const int**, which at first appears to be reasonable (after all, you're just adding a const, which is normally ok). The reason is that if you could do this, you could accidentally modify a const object:

const int x = 3;
int *px;
const int **ppx = &px;  // ERROR: conversion from 'int**' to 'const int**'
*ppx = &x;  // ok, assigning 'const int*' to 'const int*'
*px = 4;    // oops, just modified a const object

It's a very non-intuitive result, but the only way to make sure that you can't modify a const object in this case (note how there are no typecasts) is to make line 3 an error.

You're only allowed to add const without a cast at the FIRST level of indirection:

int * const *ppx = &px;  // this is ok
*ppx = &x;               // but now this is an error because *ppx is 'const'

In C++, it is impossible to modify a const object without using a typecast of some sort. You'll have to use either a C-style cast or a C++-style const_cast to remove the const-ness. Any other attempt to do so will result in a compiler error somewhere.

Adam Rosenfield
Every now and then, I work through this reasoning again. It always makes my head hurt.
Michael Burr
@Michael: phew - I'm not alone, then. Mine too!
Jonathan Leffler
A: 

The article you were looking at might have been talking about the difference between

const int *pciCantChangeTarget;
const int ci = 37;
pciCantChangeTarget = &ci; // works fine
*pciCantChangeTarget = 3; // compile error

and

int nFirst = 1;
int const *cpiCantChangePointerValue = &nFirst;
int nSecond = 968;

*pciCantChangePointerValue = 402; // works
cpiCantChangePointerValue = &ci; // compile error

Or so I recall-- I don't have anything but Java tools here, so can't test :)

mjfgates
A: 

Some of these answers point out that the compiler can optimize away the variable 'a' since it is declared const. If you really want to be able to change the value of a then you need to mark it as volatile

  const volatile int a = 3; // I promise i won't change a
  int *ptr = (int *)&a;
  (*ptr) = 5; // I'm a liar, a is now 5

Of course, declaring something as const volatile should really illustrate just how silly this is.

pk
I do not think that word ("volatile") means what you think it means. It has nothing to do with whether you can change the value, but rather insures that a read or write in the source code is made a read or write in the executable, one for one. In other words, no caching.
David Thornley
That's what i was getting at. The compiler will see const and optimize the value away. Whereas if you declare it volatile, it cannot since like you said 'no caching'.
pk
A: 
manoj