Изменение значения переменной const в С++

Я пытаюсь изменить значение переменной, которая определяется как int const, как показано ниже.

const int w = 10;
int* wp = const_cast <int*> (&w);
*wp = 20;

Значение w не изменилось и было равно 10 даже после назначения, хотя оно показывает, что оба w и wp указывают на одно и то же место памяти. Но я могу изменить значение w, если оно определено ниже, объявляя

int i = 10;
const int w = i;

Если я изменяю объявление i, чтобы сделать его const, как в

const int i = 10;

Значение w не изменяется.

В первом случае, как получилось, значение w не изменилось, хотя w и wp указывают на то же место в памяти [это было мое впечатление, которое я получаю, когда печатаю их адреса]

Какая разница с компилятором, что он рассматривает оба случая по-разному?

Есть ли способ убедиться, что w не потеряет константу, независимо от того, как она определена?

Ответы

Ответ 1

Это один из случаев, когда const const является undefined, поскольку код, вероятно, оптимизирован таким образом, что w не является действительно переменной и на самом деле не существует в скомпилированном коде.

Попробуйте следующее:

const volatile int w = 10; 
int &wr = const_cast <int &> (w); 
wr = 20; 
std::cout << w << std::endl;

Во всяком случае, я бы не советовал злоупотреблять const_cast таким образом.

Ответ 2

В приведенном выше примере код преобразуется в следующий ассемблер:

    movl    $10, 28(%esp)  //const int i = 10; 
    leal    28(%esp), %eax //int* wp = const_cast <int*>(&i);
    movl    %eax, 24(%esp) //store the pointer on the stack
    movl    24(%esp), %eax //place the value of wp in eax
    movl    $20, (%eax) //*wp  = 20; -  so all good until here
    movl    $10, 4(%esp) //place constant value 10 onto the the stack for use in printf
    movl    $.LC0, (%esp) // load string
    call    printf //call printf

Поскольку исходный int я был объявлен константой, компилятор оставляет за собой право использовать значение буквального значения вместо значения, хранящегося в стеке. Это означает, что значение не изменяется, и вы застряли с оригиналом 10.

Мораль истории - это константы времени компиляции, которые должны оставаться постоянными, потому что это то, что вы говорите компилятору. Мораль этой истории заключается в том, что изгнание совести для изменения константы может привести к плохим вещам.

Ответ 3

const_cast не отображает константу переменной, как определено. Если бы вы передали неконстантную переменную по ссылке в метод, принимающий константу, такую ​​как void foo(const int& x), тогда вы могли бы использовать const_cast для изменения значения x внутри foo, но только если переменная, которую вы на самом деле прошло, не было const в первую очередь.

Ответ 4

Почему вы не можете просто переписать константу? Поэтому вместо

const int w = 10;
int* wp = const_cast <int*> (&w);
*wp = 20;
// some code

просто введите другую константу с тем же именем

const int w = 10;
{
   const int w = 20;
   // the same code
}

Если "новая" константа должна зависеть от ее собственного значения, вы должны ввести другую константу (const int _w = w; const int w = _w * 2;). Невозможные назначения будут оптимизированы компилятором, потому что мы видели, что он сделал такую ​​оптимизацию, поскольку это причина, по которой вы задали свой вопрос.

Ответ 5

Вы не должны изменять значение const. Существует причина, по которой она const и пытается ее изменить, скорее всего, приведет к ошибкам. Если const хранится в разделе памяти только для чтения, вы получите нарушения доступа.

Ответ 6

Здесь переподготовка, следует отметить, что это в C. Это обманчиво основано на использовании переменной или указателя с использованием ключевого слова const. Это подчеркивает разницу между переменной-указателем foo и тем, как ее значение может измениться с помощью указанного ключевого слова.

char const *foo;

char * const foo;

const char *foo;

Первая и последняя декларации делают данные, на которые указывает "foo только для чтения", но вы можете изменить адрес, на который указывает "foo, например.

const *char foo; /* OR char const *foo */

char str[] = "Hello";

foo = &str[0]; /* OK! */

foo[1] = 'h'; /* BZZZZTTT! Compile Fails! */

Среднее объявление выше, делает указатель только для чтения, т.е. вы не можете изменить адрес данных, на которые указывает "foo

char * const foo;

char str[] = "Hello";

foo = &str[0]; /* BZZZZTTT! Compile Fails! */

Ответ 7

Мое предположение заключалось бы в том, что объявление w const позволяет компилятору выполнять более агрессивные оптимизации, такие как встраивание w-значений и инструкций переупорядочения. Wether w, похоже, изменится или не зависит от того, какие оптимизации были применены в точном случае и не под вашим контролем.

Вы не можете заставить w быть полностью const. Cons_cast должен быть подсказкой для программиста, что они могут делать что-то подозрительное.

Ответ 8

Хороший вопрос. Я думаю, что путаница исходит из того, что С++ использует ключевое слово "const" для двух разных концепций в зависимости от контекста. Эти понятия являются постоянными и переменными только для чтения.

Когда значение переменной const может быть вычислено во время компиляции, оно создает истинную константу. Ссылки на такую ​​константу заменяются ее значением всякий раз, когда она используется. Вот почему в памяти нет места, которое можно изменить, чтобы повлиять на все места, где оно используется. Это похоже на #define.

Когда значение переменной const не может быть вычислено во время компиляции, оно создает переменную только для чтения. Он имеет место в памяти, которое содержит значение, но компилятор обеспечивает поведение, доступное только для чтения.