ANCI C (C90): Можно ли изменить const?

Я смущен тем, что спецификация ANSI говорит об изменении переменной, объявленной const, которая может быть законно изменена через ее адрес. К сожалению, у меня нет доступа к спецификации C90, но я получил противоречивые указатели:

  1. Ключевое слово const не превращает переменную в константу! Символ с квалификатором const просто означает, что этот символ нельзя использовать для присваивания. Это делает значение читаемым только через этот символ; это не предотвращает изменение значения другими средствами, внутренними (или даже внешними) по отношению к программе. Это довольно полезно только для уточнения параметра указателя, чтобы указать, что эта функция не будет изменять данные, на которые указывает аргумент, но другие функции могут. (Эксперт по программированию на C: секреты Deep C: Питер ван дер Линден)

  2. Если предпринимается попытка изменить объект, определенный с типом, уточненным константой, путем использования lvalue с типом, не уточненным константой, поведение не определено. Если предпринимается попытка сослаться на объект, определенный с типом с уточнением volatile, посредством использования lvalue с типом с уточнением volatile, поведение не определено. (http://flash-gordon.me.uk/ansi.c.txt)

Последнее я видел в спецификации C99 (n1256.pdf).

Кто-нибудь может пояснить, какое из двух приведенных выше мнений верно?

Редактировать: Программирование Expect C фактически дает пример, демонстрирующий возможность изменения переменной const с помощью указателя.


person Quiescent    schedule 12.09.2013    source источник
comment
Почему они оба не могут быть правдой? Первый говорит о том, что константные переменные могут быть изменены внешними средствами. Второй говорит, что попытка сделать это намеренно является неопределенным поведением, насколько я понимаю.   -  person Andon M. Coleman    schedule 12.09.2013
comment
Языковые конструкции предназначены для защиты от случайного неправильного использования, а не от преднамеренного неправильного использования - я пытаюсь убедить людей в том же самом в этом вопросе - stackoverflow.com/questions/18755713/ - большинство людей, кажется, думают, что языковые конструкции можно использовать для обеспечения безопасности или предотвращения преднамеренного неправильного использования. К счастью, этот вопрос теперь закрыт как дубликат другого вопроса, на который есть более разумные ответы.   -  person user93353    schedule 12.09.2013


Ответы (6)


Не знаю насчет C90, но C11 содержит этот пункт, который, я думаю, был там с первого дня (C11, 6.7.3/6):

Если предпринимается попытка изменить объект, определенный с типом, уточненным константой, путем использования lvalue с типом, не уточненным константой, поведение не определено.

То же самое верно для volatile-квалифицированных объектов.

person Kerrek SB    schedule 12.09.2013
comment
Да, кажется, он был там с самого начала. Но мне интересно, как такая хорошая книга могла поместить это прямо под заголовком Чтение стандарта ANSI C для удовольствия, удовольствия и прибыли? - person Quiescent; 12.09.2013
comment
@ user926918: в двух цитатах, которые вы поставили в своем вопросе, нет противоречия, поэтому я не уверен, что понимаю ваш комментарий. - person Mat; 12.09.2013

Это похоже на C90 (C89), как C99.

C89 §3.5.3 Спецификаторы типов

Если предпринимается попытка изменить объект, определенный с типом, уточненным константой, путем использования lvalue с типом, не уточненным константой, поведение не определено. Если предпринимается попытка сослаться на объект, определенный с типом с уточнением volatile, посредством использования lvalue с типом с уточнением volatile, поведение не определено.

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

person Yu Hao    schedule 12.09.2013
comment
Что касается стандарта C, любая программа, вызывающая Undefined Behavior, совершенно бессмысленна. Поставщик компилятора может присваивать значения программам, значение которых не определено стандартом C, но я бы расценил утверждение о том, что определенные конструкции приводят к неопределенному поведению, эквивалентным утверждению о том, что они запрещены для любого компилятора, документация которого не явно предусмотреть их. - person supercat; 04.09.2014
comment
@supercat Чтобы писать правильный и переносимый код, всегда следует избегать неопределенного поведения. Но бывают случаи, когда невозможно использовать переносимый код для решения конкретной задачи. Например, в некоторых встроенных средах необходимо получить доступ к адресу 0. В переносимом коде делать такие вещи незаконно, но компилятор может позволить вам делать это на этой конкретной платформе. - person Yu Hao; 04.09.2014
comment
Если в документации к компилятору сказано, что произойдет, если код получит доступ к (int*)0, то это не запрещено при использовании этого компилятора. Я хочу сказать, что если стандарт говорит, что конструкция дает Undefined Behavior, а компилятор не определяет для нее поведение, то такая конструкция должна считаться запрещенной, если кто-то хочет написать осмысленный код. Было бы полезно, если бы во многих ситуациях, которые в настоящее время вызывают Undefined Behavior, комитет по стандартам либо указывал ограниченный набор вещей, которые могут произойти, либо, по крайней мере, указывал, что компилятор, который даст... - person supercat; 04.09.2014
comment
... поведение, выходящее за рамки ожидаемого диапазона вещей, которые могут произойти, должно документально подтверждать, что [например. укажите, что отсутствует документация об обратном, целочисленное переполнение даст неопределенное неопределенное значение, которое может быть за пределами нормального диапазона типа, может измениться при повторном чтении и может быть представлением ловушки, но может не имеют никакого другого эффекта, если в документации компилятора не указан конкретный эффект или явно не указано, что поведение полностью не определено. Такое правило было бы полезно в некоторых ситуациях, когда перекрестная проверка результатов нескольких вычислений... - person supercat; 04.09.2014
comment
... покажет, что одно из них было фиктивным, но код не узнает, какое вычисление было фиктивным, пока все не будет выполнено. Избегание целочисленных переполнений в вычислениях, результат которых не имеет значения, может усложнить код без реальной пользы. - person supercat; 04.09.2014

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

Но поскольку эта переменная была определена как const, изменение ее значения вызовет неопределенное поведение.

person Dayal rai    schedule 12.09.2013

Что такое декларация, например:

T const *p; //p has type “pointer to const T

означает?
Программа может использовать выражение p для изменения значения объекта-указателя, на который указывает p, но она не может использовать выражение *p для изменения значения любых объектов, на которые может указывать *p. Если в программе есть другое выражение e неполного типа, обозначающее объект, который также обозначает *p, программа все равно может использовать e для изменения этого объекта. Таким образом, программа может изменить объект прямо из выражения const-qualified.

person haccks    schedule 12.09.2013

Логично, что поведение не определено, если вы попытаетесь изменить константную переменную. Рассмотрим встраиваемые платформы, где код + константы размещаются в ПЗУ. В этом случае изменить значение просто невозможно, так как оно сгорает навсегда. Там, где все находится в оперативной памяти, ее, вероятно, можно будет изменить. Вот почему в стандарте в этом случае говорится о "неопределенном поведении" - поведение должно зависеть от платформы и компилятора.

Утверждение 1 и 2 на самом деле не исключают друг друга.

person Johan Kotlinski    schedule 12.09.2013

Почему у вас сложилось впечатление, что эти два утверждения исключают друг друга?

const — это просто квалификатор типа, в этом нет ничего волшебного.

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

На мой взгляд, в обоих утверждениях нет ничего противоречащего.

person Andon M. Coleman    schedule 12.09.2013