Можем ли мы изменить значение константной переменной?

Из этой статьи.

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

Я не понимаю, как мы можем изменить значение const переменная указателем. Разве это не неопределенное поведение?

const int a = 81;
int *p = (int *)&a;
*p = 42; /* not allowed */

5 ответов

Решение

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

void bad_func(const int *p) {
    int *q = (int *) p;            // casting away const
    *q = 42;                       // potential undefined behaviour
}

void my_func() {
    int i = 4;
    const int j = 5;
    register const int k = 6;
    bad_func(&i);                  // ugly but allowed
    bad_func(&j);                  // oops - undefined behaviour invoked
    bad_func(&k);                  // constraint violation; diagnostic required
}

Изменив потенциальный UB на нарушение ограничения, диагностика становится необходимой, и ошибка (обязательно должна быть) диагностирована во время компиляции:

c11

5.1.1.3 Диагностика

1 - Соответствующая реализация должна генерировать по крайней мере одно диагностическое сообщение [...], если блок преобразования или блок предварительной обработки содержит нарушение какого-либо синтаксического правила или ограничения, даже если поведение также явно указано как неопределенное или определенное реализацией.

6.5.3.2 Операторы адреса и косвенности

Ограничения

1 - операнд одинарного & оператор должен быть [...] lvalue, который обозначает объект, который [...] не объявлен с register спецификатор класса хранения.

Обратите внимание, что распад массива на указатель на register объект массива - неопределенное поведение, которое не требуется диагностировать (6.3.2.1:3).

Обратите также внимание, что взяв адрес register lvalue разрешено в C++, где register это просто подсказка оптимизатора (и при этом устаревшая).

Можем ли мы изменить значение const переменная?

Да, вы можете изменить const Переменные с помощью различных средств: указатель хакерства, броски и т.д...
Читайте дальше Q!

Это действительный код для изменения значения const переменная?

Нет! То, что это дает вам, является неопределенным поведением.

Технически, ваш пример кода имеет неопределенное поведение.
Программа не соответствует стандарту c после изменения const и, следовательно, может дать любой результат.

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

Стандарт C99 3.4.3 гласит:

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

П р и м е ч а н и е - Возможное неопределенное поведение варьируется от полного игнорирования ситуации с непредсказуемыми результатами до поведения во время перевода или выполнения программы документированным образом, характерным для среды (с выдачей или без выдачи диагностического сообщения), до прекращения перевода или выполнения (с выдача диагностического сообщения).

Ваш код компилируется, но имеет неопределенное поведение.

Авторская точка зрения заключается в использовании const а также register так что код больше не компилируется:

const int a = 81; 
int *p = (int *)&a; /* no compile error */
*p = 42; /* UB */ 

register const int b = 81; 
int *q = (int *)&b; /* does not compile */

Фрагмент кода действительно вызывает неопределенное поведение.

Я не совсем уверен, в чем суть автора: чтобы не позволить "чужому коду" изменить значение переменной, вы делаете это const так что... вместо этого вызывается UB? Как это предпочтительнее? Честно говоря, это не имеет смысла.

Я думаю, что автор также говорит об этом случае, который является неправильным пониманием const:

 int a = 1;
 int* const a_ptr = (int* const)&a; //cast not relevant
 int function(int* const p){
     int* malicious = (int*)p;
     *malicious = 2;
 }

Сама переменная не является константой, но указатель есть. Вредоносный код может преобразовываться в обычный указатель и юридически изменять указанную ниже переменную.

Я не понимаю, как мы можем изменить значение constпеременная указателем. Разве это не неопределенное поведение?


Да, это неопределенное поведение:

Цитата из C18, 6.7.3 / 7:

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

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

К счастью, в этом случае при компиляции fe:

#include <stdio.h>

int main(){
    const int a = 25;
    int *p = &a;

    *p = 26;

    printf("a = %d",a);
}

компилятор выдаст предупреждение:

инициализация отбрасывает квалификатор 'const' из целевого типа указателя [-Wdiscarded-qualifiers] (gcc)

или

предупреждение: инициализация 'int *' выражением типа 'const int *' отбрасывает квалификаторы [-Wincompatible-pointer-types-discards-qualifiers] (clang)

но несмотря на то, что код содержит части, которые вызывают неопределенное поведение, и вы никогда не можете быть уверены, что он напечатает при любом выполнении, вы получаете скомпилированную вредоносную программу (без -Werror вариант конечно).


Можем ли мы изменить значение const переменная?

Так что да - к сожалению. Фактически можно изменитьconstвозразить, но вы никогда не должны этого делать, ни намеренно, ни случайно.

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

Другие вопросы по тегам