возврат константного указателя на const int - PullRequest
0 голосов
/ 28 апреля 2020

В этом коде:

const int* const fun(const int *const ptr)
{
    return ptr;
}

int main()
{
    int i=9;
    int *main_ptr;
    main_ptr = fun(&i);
    return 0;
}

Компилятор предупреждает:

предупреждение: назначение отбрасывает квалификатор 'const' из целевого типа указателя [-Wdiscarded-qualifiers] main_ptr = fun (& i);

При определении main_ptr в качестве указателя на const int предупреждение исчезает (что понятно), но компилятор не жалуется на отклонение спецификатора const при ухудшении из const указатель на просто указатель.

Это поведение предупреждения в одном случае и отсутствие предупреждения в другом из-за того факта, что когда дело доходит до указателя на const int, в этом случае указанная переменная имеет вид не уничтожается, когда функция fun заканчивается, и по той же причине указатель const на часть int не имеет значения, поскольку переменная является локальной, и она уничтожается, когда fun заканчивается?

Ответы [ 2 ]

2 голосов
/ 28 апреля 2020

Квалификаторы (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 говорит, что доступ к объекту в памяти ведет себя так, как будто это одна неделимая вещь (не сделано байтов, которые могут быть прочитаны или изменены отдельно), так что при доступе из нескольких потоков всегда используется «целое» значение для него, никогда не смешивая частичные доступы из разных потоков.
0 голосов
/ 28 апреля 2020

Здесь присутствуют два «несоответствия» типов, первое из которых

main_ptr = fun(...)    // const int *const -> int *

, где мы отбрасываем const ness возвращаемого значения fun, присваивая его main_ptr и второе -

... = fun(&i)          // int * -> const int *const

, где мы передаем простой старый int * в функцию, которая принимает const int *const.

Как вы заметили, вы будете предупреждены при выполнении первого, потому что мы теряем гарантию безопасности, которую сделал тип возврата fun.

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

...