Вопрос по c++, c – Является ли const ложью? (поскольку const может быть отброшен) [duplicate]

25

Possible Duplicate:
Sell me on const correctness

В чем полезность ключевого слова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;
}

Output: 0

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

@ К-балл Я не могу понять, почему так много хороших ответов - таких как твой! - добавлены в качестве комментария & # x2026; kay
Обратите внимание, что хотя это нормально для вашего конкретного случая,casting away the const иногдаundefined behaviour K-ballo
@ К-балл Да, UB происходит от задания. Captain Obvlious
cast и все возможно :) Op De Cirkel
@ K-балл, но в данном случае это явно Captain Obvlious

Ваш Ответ

5   ответов
6

Ваш...

int n = 1;

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

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

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

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

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

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

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

44

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() нарушает свой контракт, происходят странные вещи. Не нарушайте договор. И будьте рады, что компилятор поможет вам сохранить контракт. Слепки злые.

Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededmodifyError: User Rate Limit Exceeded
Error: User Rate Limit ExceedednError: User Rate Limit ExceededmainError: User Rate Limit ExceededmainError: User Rate Limit ExceededmainError: User Rate Limit ExceededprintfError: User Rate Limit Exceeded1Error: User Rate Limit ExceedednError: User Rate Limit Exceededconst
Error: User Rate Limit ExceededconstError: User Rate Limit Exceeded
Error: User Rate Limit ExceededconstError: User Rate Limit ExceededconstError: User Rate Limit Exceededstd::unique_ptrError: User Rate Limit Exceeded
1

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

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

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

Error: User Rate Limit ExceededthinkError: User Rate Limit Exceededconst objectError: User Rate Limit Exceededconst dataError: User Rate Limit ExceededconstError: User Rate Limit ExceededmutableError: User Rate Limit Exceededconst_castError: User Rate Limit Exceededconst int i = 3Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededmsdn.microsoft.com/en-us/library/bz6at95h(v=vs.100).aspx .
Error: User Rate Limit Exceededconst_castError: User Rate Limit Exceeded
const_castError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
10

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

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

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

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 (ака нарушение доступа) вconst_is_a_lie поскольку компилятор / среда выполнения могут хранить строковые литеральные значения на страницах памяти, которые не доступны для записи.

Стандарт говорит об изменении константных объектов.

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

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

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

2

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

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

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

Похожие вопросы