Квалификаторы (const
, volatile
, restricted
и _Atomic
) применяются только к lvalues (указателям объектов в памяти), но не к значениям (данные в выражениях). По C 2018 6.7.3 5:
Свойства, связанные с квалифицированными типами, имеют смысл только для выражений, которые являются lvalues.
Рассмотрим const int x = 3; int y = x;
. Это разрешено const
является ограничением для x
. После считывания значения x
результатом будет просто int
3; const
не привязан к 3. Затем мы можем установить значение y
равным 3, и нет требования, чтобы y
не изменялся после этого. const
не придерживается значения, и компилятор не предупредит нас об этом.
Объявление const int* const fun(const int *const ptr)
говорит, что значение , возвращаемое fun
, равно const
. Это бессмысленно, и хороший компилятор может предупредить, что const
перед fun
не имеет никакого эффекта. Несмотря на это, этот квалификатор отбрасывается при оценке вызова функции, поэтому нет проблем с присвоением возвращаемого значения этой функции указателю, который не является const
.
Мы можем рассмотреть более простой пример:
int t;
const int * const x = &t;
const int * y = x;
Компилятор не предупреждает об этом, потому что y
получает только значение x
. Тот факт, что x
не может измениться, не препятствует нашей способности присвоить его значение y
, которое может измениться.
Дополнение
Когда lvalue используется для его значения в выражение, все его классификаторы отбрасываются. C 2018 6.3.2.1 2 говорит:
За исключением случаев, когда это операнд оператора sizeof
, унарный оператор &
, оператор ++
, оператор --
или левый операнд оператора .
или оператора присваивания, lvalue, у которого нет типа массива, преобразуется в значение, сохраненное в назначенном объекте (и больше не является lvalue); это называется преобразованием lvalue. Если lvalue имеет квалифицированный тип, значение имеет неквалифицированную версию типа lvalue; …
Каждый классификатор говорит о том, как будет обрабатываться объект в памяти :
const
говорит, что объект в памяти не будет изменено (через это lvalue). volatile
говорит, что объект в памяти может измениться средствами, неизвестными реализации C, или что доступ к нему может вызвать эффекты, неизвестные компилятору C. restrict
говорит, что доступ к объекту в памяти возможен только средствами, полученными из этого конкретного lvalue. _Atomic
говорит, что доступ к объекту в памяти ведет себя так, как будто это одна неделимая вещь (не сделано байтов, которые могут быть прочитаны или изменены отдельно), так что при доступе из нескольких потоков всегда используется «целое» значение для него, никогда не смешивая частичные доступы из разных потоков.