Ну просто посмотрите на сгенерированную сборку ...
const int j = 100;
00052F50 mov dword ptr [j],64h
//int *q = &j; //Compiler disallows
int *q = (int*)&j; //By some magic, now allowed
00052F58 lea rax,[j]
00052F5D mov qword ptr [q],rax
*q = 300; //After this line, j = 300 in debugger
00052F62 mov rax,qword ptr [q]
00052F67 mov dword ptr [rax],12Ch
cout << "j = " << j << endl; //300 in debugger, 100 in console
00052F6D lea rdx,[__xt_z+114h (07FF679CC6544h)]
00052F74 lea rcx,[std::cout (07FF679D31B80h)]
00052F7B call std::operator<<<std::char_traits<char> > (07FF679B43044h)
00052F80 mov edx,64h
00052F85 mov rcx,rax
00052F88 call std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF679B417E9h)
00052F8D lea rdx,[std::endl<char,std::char_traits<char> > (07FF679B42C25h)]
00052F94 mov rcx,rax
00052F97 call std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF679B445F7h)
//^ What is happening here? Where are the two values stored?
cout << "*q = " << *q << endl; //300 in both
00052F9C lea rdx,[__xt_z+11Ch (07FF679CC654Ch)]
00052FA3 lea rcx,[std::cout (07FF679D31B80h)]
00052FAA call std::operator<<<std::char_traits<char> > (07FF679B43044h)
00052FAF mov rcx,qword ptr [q]
00052FB4 mov edx,dword ptr [rcx]
00052FB6 mov rcx,rax
00052FB9 call std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF679B417E9h)
00052FBE lea rdx,[std::endl<char,std::char_traits<char> > (07FF679B42C25h)]
00052FC5 mov rcx,rax
00052FC8 call std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF679B445F7h)
Обратите внимание на "странное" прочтение из __xt_z+114h
. Это смещение от конца глобальных инициализаторов (__xt_z
, вероятно, является ближайшим символом, найденным отладчиком), скорее всего, в разделе данных только для чтения (.rdata
).
Вот где версия Debug ставит 100
(в конце концов, это константа).
Затем версия отладки MSVC всегда выделяет локальные переменные и константы в стеке, следовательно, вы получаете отдельную переменную j
, которую вы можете даже изменить (обратите внимание, что компилятору не нужно читать из нее, когда вы читаете j
, поскольку он знает, что j
является константой, содержащей 100
).
Если мы попробуем то же самое в режиме Release, мы увидим, что компилятор сделал распространение значений и оптимизировал обе переменные, просто вставив значения в код:
const int j = 100;
//int *q = &j; //Compiler disallows
int *q = (int*)&j; //By some magic, now allowed
*q = 300; //After this line, j = 300 in debugger
cout << "j = " << j << endl; //300 in debugger, 100 in console
000C101D lea rdx,[string "j = " (07FF72FAC3298h)]
000C1024 mov rcx,qword ptr [__imp_std::cout (07FF72FAC30A8h)]
000C102B call std::operator<<<std::char_traits<char> > (07FF72FAC1110h)
000C1030 mov edx,64h
000C1035 mov rcx,rax
000C1038 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF72FAC30A0h)]
000C103E lea rdx,[std::endl<char,std::char_traits<char> > (07FF72FAC12E0h)]
000C1045 mov rcx,rax
000C1048 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF72FAC3098h)]
//^ What is happening here? Where are the two values stored?
cout << "*q = " << *q << endl; //300 in both
000C104E lea rdx,[string "*q = " (07FF72FAC32A0h)]
000C1055 mov rcx,qword ptr [__imp_std::cout (07FF72FAC30A8h)]
000C105C call std::operator<<<std::char_traits<char> > (07FF72FAC1110h)
000C1061 mov edx,12Ch
000C1066 mov rcx,rax
000C1069 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF72FAC30A0h)]
000C106F lea rdx,[std::endl<char,std::char_traits<char> > (07FF72FAC12E0h)]
000C1076 mov rcx,rax
000C1079 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF72FAC3098h)]
В обоих случаях вывод одинаковый. Переменная const
остается неизменной.
Имеет ли это значение? Нет, вы не должны полагаться на это поведение и не должны изменять константы.