Что является результатом эталонного оператора «и» на константные переменные? - PullRequest
7 голосов
/ 14 ноября 2011

Меня спросили, как можно изменить значение переменной const.

Мой мой очевидный ответ был "указатели!"но я попробовал следующий кусок кода, и я озадачен ...

int main()
{
    const int x = 5;
    int *ptr = (int *)(&x); // "Cast away" the const-ness..
    cout << "Value at " << ptr << ":"<< (*ptr) <<endl;
    *ptr = 6;
    cout << "Now the value of "<< ptr << " is: " << (*ptr) <<endl;
    cout << "But the value of x is still " << x <<endl;
    return 0;
}

И вывод был:

Value at <some address> :5
Now the value of <same address> is: 6
But the value of x is still 5

Теперь я не уверен, что именно возвращаетсяиз '& x', но это определенно не фактический адрес x, так как значение в x не изменилось!

Но с другой стороны, ptr содержит в начале значение x!Итак, что именно?

РЕДАКТИРОВАТЬ скомпилировано с VS2010

Ответы [ 5 ]

15 голосов
/ 14 ноября 2011

Ваша программа вызывает неопределенное поведение (запись в константную переменную через указатель - неопределенное поведение), поэтому может произойти все что угодно. Это, как говорится, наиболее вероятное объяснение, почему вы получаете поведение, которое вы видите в вашей конкретной реализации:

Когда вы делаете &x, вы получаете адрес x. Когда вы делаете *ptr = 6, вы записываете 6 в x ячейку памяти. Однако, когда вы делаете cout << x, вы фактически не читаете из области памяти x, потому что ваш компилятор оптимизировал код, заменив x на 5 здесь. Поскольку x - это const, компилятору разрешено это делать, поскольку не существует легальной программы на C ++, в которой это могло бы изменить поведение программы.

2 голосов
/ 14 ноября 2011

Компилятор кэширует x в регистре, поэтому значение в памяти изменяется, но последняя распечатка остается прежней. Проверьте сгенерированную сборку (скомпилируйте с -s).

2 голосов
/ 14 ноября 2011

Прежде всего, это поведение не определено.Тем не менее, вот что, вероятно, происходит:

Когда вы делаете это:

int *ptr = (int *)(&x);

5 хранится по какому-то адресу где-то.Вот почему указатель, кажется, работает правильно.(хотя отбрасывание const по-прежнему не определено)

Однако из-за оптимизации компилятора x = 5 просто вставляется в качестве литерала в конечном операторе печати.Компилятор считает, что это безопасно, потому что объявлено x const.

cout << "But the value of x is still " << x <<endl;

Вот почему вы печатаете исходное значение 5.

1 голос
/ 14 ноября 2011

Возможно, вы испытываете побочный эффект оптимизации кода, попробуйте запустить тот же код, отключив всю оптимизацию, или проверьте код, сгенерированный asm.Я полагаю, что компилятор повторно использует значение, которое он имеет в некотором реестре, вместе с функцией, поскольку он делает ставку на const, поэтому даже если вы действительно изменяете значение, измененное значение не распространяется должным образом.Причины этого, как заметил Кит в комментах, в том, что вы не уверены в своем поведении.

0 голосов
/ 14 ноября 2011

То, что возвращается из &x, это указатель на const int (то есть int const*).Теперь указатели встроены как удерживающие адрес, но указатели являются не адресами, и ваш пример довольно хорошо показывает, почему: тип указателя, хотя и отсутствует во время выполнения, все еще играет важную роль.

В вашем случае вы отбрасываете const и таким образом лжете компилятору "этот указатель указывает на неконстантное int".Однако компилятор знает из объявления, что значение x не может измениться (оно было объявлено как const), и свободно использует этот факт (и стандарт это позволяет: ваша попытка изменить его с помощью указателя на неконстантное intявляется неопределенным поведением, и поэтому компилятору разрешено делать все что угодно).

...