Почему незаконно создавать указатель на указатель, который указывает на const в C? - PullRequest
0 голосов
/ 15 января 2019

Это источник распространенной ошибки «отброшенный константный квалификатор при назначении». Однако я не понимаю, почему это незаконно?

Рассмотрим этот код,

int sum_array(const int *a, int n){
   int sum, *i;
   sum = 0;
   for(i = a; i<a+n; i++)
       sum += *i;
   return sum;
}

Очевидно, что я могу сделать ту же операцию, используя i = 0 и сравнивая a + i < a+n; однако для меня не имеет смысла, почему просто копировать адрес переменной в другую переменную недопустимо?

Обычно переменная const указывает, что значение переменной не может быть изменено. Например. const int x = 7, здесь мы заявляем, что значение x не должно изменяться с 7.

Однако с помощью указателей const создание «потенциала» для изменения переменной также недопустимо. То есть i = a не меняет значение того, на что указывает точка, что на самом деле было бы «незаконным», было бы * i = 0, однако i = a по-прежнему недопустимо, потому что это дает нам возможность изменить a.

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

Ответы [ 3 ]

0 голосов
/ 15 января 2019

const служит двум целям:

  1. в контракте вызова функции (здесь int sum_array(const int *a, int n)) это означает, что аргумент a не будет использоваться функцией для изменения содержимого, указанного a (a не является константой , это указатель на вещи, считающиеся постоянными). Затем вызывающая сторона знает, что данные не будут изменены через вызов, и компилятор должен принудительно применить их, как может:

  2. внутри определения функции, тогда разработчик защищен от случайного изменения содержимого, указанного a. то есть. если он попытается написать что-то вроде *a = someValue;, компилятор будет жаловаться на нарушение контракта. Но затем он также подтверждает, что этот контракт в любом случае соблюдается. Таким образом, любая производная переменная из a, которая может предоставить доступ к тем же данным, должна рассматриваться как const, иначе договор будет нарушен. Тогда запись int *p = a является явным нарушением этого, потому что p не является указателем на постоянные данные, что позволило бы вам написать *p = someValue для изменения данных.

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

const int *a...
int *p = a;

запрещено, но

int *a...
const int *p = a;

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

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

const *a...;
int *p = (int *)a; // "seriously refrain" advice
0 голосов
/ 15 января 2019

Вопрос предполагает, что, учитывая const int *a, int *i = a; может быть разрешено, а последующее *i = 0; будет запрещено («незаконно»).

Это неосуществимо, поскольку требует, чтобы компилятор отслеживал информацию об источнике данных в i. В точке, где появляется *i = 0;, компилятор должен знать, что i содержит значение, полученное в результате инициализации из a.

Мы могли бы сделать это очень простым кодом. Но учтите, что код может быть очень сложным, с циклами, ветвями и вызовами функций. В момент появления *i = 0; компилятор, как правило, не может знать, содержит ли i адрес из a или он был изменен на что-то другое. (Я ожидаю, что это эквивалентно проблеме остановки и, следовательно, вообще логически невозможно).

C использует типы для управления этим. Когда a является указателем на const, он может быть назначен только указателю на const (если не переопределено явным приведением). Поэтому i должен быть указателем на const, что позволяет компилятору знать, что объект, на который он указывает, не должен изменяться.

0 голосов
/ 15 января 2019

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

.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...