Подтвердить что ты не робот

Является ли const ложью? (поскольку const может быть отброшен)

Возможный дубликат:
Продайте меня на const correct

Какова польза ключевого слова const в C или C++, так как это разрешило такую ​​вещь?

void const_is_a_lie(const int* n)
{ 
    *((int*) n) = 0;
}

int main()
{
    int n = 1;
    const_is_a_lie(&n);
    printf("%d", n);
    return 0;
}

Выход: 0

Ясно, что const не может гарантировать нечувствительность аргумента.

4b9b3361

Ответ 1

const - это обещание, которое вы предоставляете компилятору, а не то, что оно гарантирует вам.

Например,

void const_is_a_lie(const int* n)
{ 
    *((int*) n) = 0;
}

#include <stdio.h>
int main()
{
    const int n = 1;
    const_is_a_lie(&n);
    printf("%d", n);
    return 0;
}

Результат, показанный на http://ideone.com/Ejogb,

1

Из-за const компилятору разрешено предположить, что значение не изменится, и поэтому оно может пропустить его перечитание, если это ускорит выполнение программы.

В этом случае, поскольку const_is_a_lie() нарушает его контракт, происходят странные вещи. Не нарушайте контракт. И будьте рады, что компилятор дает вам помощь в сохранении контракта. Каста зла.

Ответ 2

В этом случае n является указателем на константу int. Когда вы отбрасываете его на int*, вы удаляете классификатор const, и поэтому операция разрешена.

Если вы сообщите компилятору удалить классификатор const, он с радостью сделает это. Компилятор поможет убедиться, что ваш код верен, если вы позволите ему выполнять свою работу. Извлекая constness, вы сообщаете компилятору, что знаете, что цель n не постоянна, и вы действительно хотите ее изменить.

Если вещь, на которую указывает ваш указатель, фактически была объявлена ​​ const, тогда вы вызываете поведение undefined, пытаясь ее изменить, и что-то может произойти. Это может сработать. Операция записи может быть не видна. Программа может потерпеть крах. Ваш монитор может ударить вас. (Хорошо, вероятно, не последний).

void const_is_a_lie(const char * c) {
    *((char *)c) = '5';
}

int main() {
    const char * text = "12345";
    const_is_a_lie(text);
    printf("%s\n", text);

    return 0;
}

В зависимости от вашей конкретной среды может быть segfault (aka нарушение прав доступа) в const_is_a_lie, поскольку компилятор/среда выполнения могут хранить строковые литералы на страницах памяти, которые не доступны для записи.

Стандарт должен сказать об изменении объектов const.

7.1.6.1/4 cv-qualifiers [dcl.type.cv]

За исключением того, что любой член класса, объявленный mutable (7.1.1), может быть изменен, любая попытка изменить объект const во время его жизни (3.8) приводит к поведению undefined

"Доктор, мне больно, когда я это делаю!" "Так не делай этого".

Ответ 3

Ваш...

int n = 1;

... обеспечивает существование n в памяти чтения/записи; это переменная non const, поэтому последующая попытка ее изменения будет определять поведение. Учитывая такую ​​переменную, вы можете иметь сочетание указателей и ссылок const и/или не const к ней - константа каждого - это просто способ для программиста защитить от случайного изменения в этой "ветки" код. Я говорю "ветвь", потому что вы можете визуализировать доступ, предоставляемый n, как дерево, в котором - после того, как ветвь отмечена const, все подсекции (дальнейшие указатели/ссылки на n содержат ли дополнительные локальные переменные, параметры функции и т.д., инициализированные оттуда), должны оставаться const, если, конечно, вы явно не отбросили это понятие о постоянной. Отбрасывание const безопасно (если это потенциально запутанно) для переменных, которые изменяются, как ваш n, потому что они в конечном счете все еще записывают обратно в адрес памяти, который может быть изменен/изменен/не const. Все причудливые оптимизации и кэширование, которые вы можете себе представить, вызывают проблемы в этих сценариях, не допускаются, как требует стандарт, и гарантируют разумное поведение в случае, который я только что описал.

К сожалению, также возможно отбросить константу поистине неотъемлемо const переменных, например say const int o = 1;, и любая попытка их изменения будет иметь поведение undefined. Для этого есть много практических причин, в том числе право компилятора разместить их в памяти, а затем только для чтения (например, см. UNIX mprotect(2)), так что попытка записи вызовет ловушку/прерывание CPU или прочитает из переменной всякий раз, когда требуется первоначальное заданное значение (даже если идентификатор переменной никогда не упоминался в коде с использованием значения), или используйте копию исходного значения на основе компиляции - игнорируя любое изменение времени выполнения самой переменной. Итак, Стандарт оставляет поведение undefined. Даже если они будут изменены, как вы могли бы предположить, остальная часть программы будет иметь поведение undefined после этого.

Но это не должно удивлять. Это та же ситуация с типами - если у вас есть...

double d = 1;
*(int*)&d = my_int;
d += 1;

... Вы солгали компилятору о типе d? В конечном счете d занимает память, которая, вероятно, не отображена на аппаратном уровне, поэтому весь компилятор когда-либо имеет перспективу, перетасовывая битовые шаблоны в и из. Но в зависимости от значения my_int и двойного представления на вашем оборудовании вы можете создать недопустимую комбинацию бит в d, которая не представляет никакого действительного двойного значения, так что последующие попытки прочитать память обратно в регистр CPU и/или что-то сделать с d, например += 1, имеют поведение undefined и могут, например, генерировать прерывание/прерывание CPU.

Это не ошибка на C или С++... они предназначены для того, чтобы вы могли делать сомнительные запросы своего оборудования, чтобы, если вы знаете, что делаете, вы можете делать какие-то странные, но полезные вещи, и редко нужно вернуться на язык ассемблера, чтобы написать код низкого уровня, даже для драйверов устройств и операционных систем.

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

Ответ 4

Система типов должна помочь, а не нянчить вас. Вы можете обойти систему типов по-разному, не только в отношении const, и каждый раз, когда вы делаете то, что вы делаете, вы берете одну безопасность из своей программы. Вы можете игнорировать const-correctness или даже базовую систему типов, проходя void* вокруг и при необходимости отбрасывая. Это не означает, что const или типы являются ложью, только чтобы вы могли пробиться через компилятор.

const существует как способ сделать компилятор осведомленным о контракте вашей функции и позволить ему не нарушать его. Точно так же, как набираемая переменная существует, так что вам не нужно угадывать, как интерпретировать данные, поскольку компилятор вам поможет. Но ребенок не будет сидеть, и если вы заставите свой путь и скажите ему, чтобы удалить константу или как данные должны быть получены, компилятор просто позволит вам, ведь вы все-таки разработали приложение, кто это второй угадайте свое суждение...

Кроме того, в некоторых случаях вы могли бы вызвать поведение undefined, и ваше приложение может даже сбой (например, если вы отбросили const из объекта, который действительно const, и вы измените объект, вы можете обнаружить, что сторона эффекты не видны в некоторых местах (компилятор предположил, что значение не изменится и, таким образом, будет выполняться с постоянной сгибанием), или приложение может потерпеть крах, если константа была загружена на страницу памяти только для чтения.

Ответ 5

Никогда const гарантировать неизменность: стандарт определяет a const_cast, который позволяет изменять данные const.

const полезен для вас, чтобы объявить больше намерения и избежать изменения данных, которые вы должны читать только. Вы получите ошибку компиляции, прося вас дважды подумать, если вы это сделаете иначе. Вы можете изменить свое мнение, но это не рекомендуется.

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