Ответ 1
Давайте начнем с очевидного: некоторые из них зависят от платформы и компилятора.
Для начала см. эту статью в Явное преобразование типов и, в частности:
Указатель на объект типа
const
может быть помещен в указатель на неconst
. Результирующий указатель будет ссылаться на оригинал object. Объект типаconst
или ссылка на объект объекта Типconst
может быть переведен в ссылку на типconst
. результирующая ссылка будет ссылаться на исходный объект. Результат попытка изменить этот объект с помощью такого указателя или ссылки будет либо вызывать исключение адресации, либо быть таким же, как если бы исходный указатель или ссылка ссылались на неконстантный объект. это реализация зависит от того, существует ли исключение адресации.
Итак, это объясняет, почему он может позволить вам изменять переменную без сукинга.
Обратите внимание, что вы можете добиться того же самого с помощью операторов трансляции, как и то, что сделает компилятор для вас, как описано в этой статье о операторах трансляции с порядком приоритета.
Однако реальный трюк здесь находится в модели памяти. Статически назначенная переменная, такая как const int a, на самом деле никогда не может иметь никакого "физического" местоположения в памяти и просто заменяется на месте в время компиляции. (Я пытаюсь перенести палец на фактическую ссылку для этого, но до сих пор самое близкое и лучшее, что я мог бы захватить, было это (очень приятно) SO answer to - это память, выделенная для статической переменной, которая никогда не используется? - Если кто-либо найдет фактическую ссылку, сообщите нам об этом.)
Итак, вот компилятор просто вас обманывает и пытается как можно больше понять вашу арифметику указателя, но в итоге заменяет фактические значения для 2 последних частей вашего второго вызова cout
a
.