Страницы

Поиск по вопросам

понедельник, 25 ноября 2019 г.

Изменение констант базовых типов


Хотелось бы понять, как С++ обрабатывает константы базовых типов. 

Что будет, если при помощи плясок с бубном и указателями изменить значение, находящееся в ячейке памяти, где, по идее, и должна содержаться константа?

Есть следующий код:

#include 

using namespace std;

int main(int argc, char** argv) {
    const int number = 45;
    const int * constPoint = &number;
    cout << "constPoint " << constPoint << endl;
    int * point = (int *) constPoint;
    cout << "     point " << point << endl;
    * point = 54;
    cout << "&number " << &number << "; number " << number << endl;
    cout << "  point " << point   << "; *point " << *point << endl;
}


Вывод у меня получается следующий: 

constPoint 0x7fff1b73a6ec
     point 0x7fff1b73a6ec
&number 0x7fff1b73a6ec; number 45
  point 0x7fff1b73a6ec; *point 54


Таким образом, значение константы не изменилось, хотя число, которое содержится в ячейке по адресу этой константы, уже другое.

Как объяснить подобное поведение?

Я не собираюсь такой код никогда использовать на практике, просто хочу понять, где хранятся константы.
    


Ответы

Ответ 1



Для начала: по стандарту, изменение при помощи трюков с указателями констант есть undefined behaviour. Может случится всё, что угодно в любой точке программы. Компилятор имеет право не хранить константу вообще нигде, если вы к ней не обращаетесь Или имеет право хранить, если ему покажется так лучше. Или хранить в памяти только для чтения. Или хранить по общему адресу с какой-нибудь инструкцией кода, которая численно равна этой же константе. Например, если компилятор видит обращение к константе, он может встроить её в точк вызова (он ведь знает значение на этапе компиляции), может обратиться к этой констант по адресу в любой момент, может даже выразить другие константы через эту (например, если у вас есть константа 1024, то обращение к 2048 он имеет право представить как 1024 << 1 из соображений эффективности). Хуже того, если где-то компилятор выяснил для себя, что константа чётная, и её младши бит равен 0, и на основании этой информации смог исключить какие-нибудь проверки из кода, то теперь проверки не пройдут, и код может крешнуться в любой момент. На UB обычно основаны дыры в безопасности программ. Например, если компилятор обоснованн считает, что длина строки 45, и под неё достаточно выделить буфер такой длины, в то время как вы, обманув компилятор, подсунете ему строку длиной 54, получится классический срыв стека. Резюме: У компилятора нет никаких «обязательств» или «принципов» по работе с константами Он имеет право делать что угодно. Наоборот, это программист, если уж обещал, что какое-то значение есть константа, не имеет права его менять.

Ответ 2



Согласно стандарту C++ (7.1.6.1 The cv-qualifiers) 4 Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior Есть один анахронизм в C, когда даже попытка изменить неконстантный объект приводит к неопределенному поведению программы. Речь идет о строковых литералах в языке C. В C++ строковые литералы имеют типы константных символьных массивов. Например, строковы литерал "Hello" имеет тип const char[6]. Однако в C строковые литералы имеют типы неконстантны символьных массивов. Поэтому этот же самый строковый литерал "Hello" в C имеет тип char[6]. Тем не менее вы не можете изменить строковый литерал в C, также как вы не можете изменить строковый литерал в C++. Из стандарта C (6.4.5 String literals) 7 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined. То, что в C строковые литералы имеют типы неконстантных символьных массивов, очевидн связано с поддержкой уже существующей кодовой базы на момент введения квалификатора const в стандарт языка C.

Ответ 3



Компилятор умный и знает, что константу можно заинлайнить. Но есть возможность убедить его, что эта константа не слишком константная: volatile const int number = 45; Полный код: http://codepad.org/tNlZoNkE Вариант с кастом в void* для вывода: http://codepad.org/EoqOgd6t PS: Насколько я помню, volatile const это корректная ситуация, которая не должн приводить к undefined behavior. он имеет полное право предполагать, что свой код точно не меняет const-объекты. Для volatile это неважно. Представим, что есть нечто внешнее, что "совершенно случайно меняет ту же константу на те же значения, что мы написали. А в самой программе вместо изменяющего кода стоят nop'ы. Такой вариант абсолютно точно корректен и не вызывает UB. Значит, если UB есть, то его вызывает не изменившееся значение, а сама операция записи Есть ли какой-то случай, в котором это возможно? Приходит в голову только read-onl страница в памяти, но может ли компилятор так поступить? Локальную переменную он поместит в стек, который однозначно не readonly. Остаются глобальные. (Поля объектов попадают в одну из этих категорий.) Или есть ещё ситуации, когда операция записи может что-то испортить?

Комментариев нет:

Отправить комментарий