Const и указатели в C
Использование константы с указателем может привести к тому, что пункт не будет изменяться, разыменовывая его, используя соответствующий указатель. Но почему я не могу изменить то, на что указатель прямо не указывает?
Например:
int a = 3;
const int* ptr = &a;
*ptr = 5;
не будет компилироваться. Но почему
*(ptr + 2) = 5;
тоже не компилируются? Я не изменяю то, на что указывает указатель.
Итак, мы должны сказать, что использование константы с указателем таким образом не только не модифицирует то, на что указывает указатель (путем разыменования указателя), но также что-нибудь еще, к которому мы получаем адрес, указатель?
Я знаю, что в примере я пытаюсь получить доступ к не выделенной памяти, но это только ради обсуждения.
Ответы
Ответ 1
ptr +2
просто имеет тот же тип, что и ptr
, а именно - указатель на объект const
.
Арифметика указателя предполагает, что объект, на который указывает, представляет собой массив всех одинаковых базовых типов. Этот тип включает квалификацию const
.
Ответ 2
Немодифицируемость, введенная const
, зависит от того, где написано const
.
Если вы пишете
const int * ptr = &a;
(или int const * ptr = &a;
), const
относится к указателю, поэтому использование указателя для записи в пункт назначения запрещено.
(OTOH, если вы писали
int * const ptr = &a;
вы не можете изменить ptr
.)
В вашем случае все, что связано с записью в пункт назначения, запрещено.
Это включает в себя *ptr
и ptr[0]
(которые эквивалентны), но также и все, что связано с изменением адреса назначения, например *(ptr + 2)
или ptr[2]
.
Ответ 3
Хотя другие ответы объясняют технические вопросы, почему это не работает, я хотел бы предложить более общую причину: это единственное, что имеет смысл.
Проблема заключается в том, что для компилятора нет общего способа решить, является ли p + something
таким же, как p
, или нет, потому что something
может быть сколь угодно сложным. Правило типа "Значения, на которые указывают p
и p + 0
, не подлежат модификации, но другие значения могут быть изменены" нельзя проверить во время компиляции: представьте, если бы вы написали:
*(p + very complex expression) = ...
должен ли ваш компилятор выяснить, равен ли very complex expression
нулю? Что насчет
int s;
scanf("%d", &s);
*(p + s) = ...
Что должен делать компилятор в этом случае?
Единственный разумный выбор здесь заключался в том, чтобы сделать любую ценность доступной через p
немодифицируемым.
Ответ 4
Так как указатель также можно использовать в качестве массива (подумайте о argv
), компилятор ограничивает каждый доступ, в котором задействован указатель. Таким образом, весь массив доступен только для чтения.
Ответ 5
Подумайте о типе выражений.
const int* ptr = &a;
Тип ptr
- const int*
.
Таким образом, тип *ptr
равен const int
. Не поддается изменению.
Тип (ptr + 2)
все еще const int*
, поэтому тип *(ptr + 2)
равен const int
, который снова не модифицируется.