Можете ли вы назначить указатели разных типов друг другу? - PullRequest
0 голосов
/ 19 сентября 2018

Учитывая

T1 *p1;
T2 *p2;

Можем ли мы назначить p1 для p2 или наоборот?Если это так, можно ли это сделать без кастинга или мы должны использовать кастинг?

Ответы [ 2 ]

0 голосов
/ 19 сентября 2018

Сначала давайте рассмотрим назначение без приведения.C 2018 6.5.16.1 1 перечисляет ограничения для простого присваивания, говоря, что одно из них должно выполняться.Первые два относятся к арифметике, структуре и типам объединения.Последние две сделки включают в себя константы нулевого указателя или _Bool.Средние два имеют дело с назначением указателей указателям:

  • левый операнд имеет атомный, квалифицированный или неквалифицированный тип указателя, и… оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов,и тип, на который указывает левый, имеет все квалификаторы типа, на который указывает правый

  • левый операнд имеет атомный, квалифицированный или неквалифицированный тип указателя, и ... один операнд являетсяуказатель на тип объекта, а другой - указатель на квалифицированную или неквалифицированную версию void, а тип, на который указывает левый, имеет все квалификаторы типа, на который указывает правый

Последний говорит, что мы можем присвоить void * любому указателю объекта и наоборот, если не удаляются квалификаторы (const, volatile, restrict или __Atomic).

Бывший говорит, что мы можем назначать указатели совместимым типам, если не удаляются квалификаторы.Что такое совместимые типы?

6.2.7 1 говорит:

  • Два типа совместимы, если они одинаковы.
  • Дополнительные правила приведены в 6.7.2,6.7.3 и 6.7.6.
  • Два типа структуры, объединения или перечисления, объявленные в отдельных единицах перевода, совместимы, если они, по сути, объявлены одинаково.(Интересно, что два таких типа, объявленные в единице перевода , одинаковые , несовместимы.)

6.7.2 4 говорит, что каждый перечислимый тип (тип enum) совместим сопределяемый реализацией выбор char или целочисленный тип со знаком или без знака.Таким образом, указатель на некоторый enum может быть назначен указателю на один char или целочисленный тип (и наоборот), но вы не можете знать какой, не зная что-то о конкретной реализации языка Си.

6.7.3 11 говорит, что квалифицированные типы должны иметь одинаковые квалификаторы для совместимости.Таким образом, int несовместим с const int, и это препятствует назначению int * для const int *.

6.7.6.1 2 говорит о совместимости двух типов указателей, онидолжны быть идентично квалифицированные указатели на совместимые типы.Это говорит нам, например, что int * несовместим с char *, и, следовательно, в соответствии с вышеизложенными ограничениями, char ** не может быть назначен для int **.

6.7.6.2 6 говорит о совместимости двух типов массивов, они должны иметь совместимые типы элементов и, если они оба имеют целочисленные постоянные размеры, они должны быть одинаковыми.(Это позволяет совместить массив с неизвестным размером с массивом известного размера. Однако дополнительный текст говорит, что если массивы в конечном итоге имеют разные размеры, их использование в контексте, требующем их совместимости, имеет неопределенное поведение.присвоение указателей таким массивам может удовлетворять его ограничениям и компилироваться без ошибок, но результирующая программа может вести себя некорректно.)

6.7.6.3 15 представлены несколько сложные правила совместимости типов функций.Эти правила сложны, потому что функции могут быть объявлены с или без списков параметров, с эллипсами и так далее.Я опущу их полное обсуждение.

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

6.5.4 обсуждает приведение типов.Его ограничения не ограничивают, какие типы указателей могут быть преобразованы в какие другие типы указателей.(Они запрещают другие вещи, включающие указатели, такие как преобразование типа указателя в тип с плавающей запятой.) Таким образом, вы можете указать любое преобразование указателя, которое вы хотите в приведении, и, пока результирующий тип совместим с типом длякоторому оно присваивается, никакое присваивание или ограничение приведения не нарушаются.Однако все еще остается вопрос о том, является ли преобразование правильным.

6.3.2.3 определяет правила для преобразования указателей.Те, кто занимается преобразованием указателей в указатели (исключая целые числа и константы нулевого указателя), говорят:

  • Любой указатель на тип объекта (не на типы функций) может быть преобразован в указатель на voidи наоборот.Результат преобразования указателя объекта в пустой указатель и обратно сравнивается равным оригиналу.

  • Указатель может быть преобразован в тот же тип с большим количеством классификаторов, а результат сравнения равеноригинал.

  • Указатель на тип объекта может быть преобразован в указатель на другой тип объекта, если результирующий указатель правильно выровнен для его типа (в противном случае поведение не определено),При обратном преобразовании результат сравнивается равным исходному указателю.(Обратите внимание, что, хотя вам разрешено выполнять это преобразование, в этом правиле не говорится, что результирующий указатель может использоваться для доступа к объекту нового типа. В C существуют другие правила об этом.)

  • Указатель на тип функции может быть преобразован в указатель на другой тип функции.При обратном преобразовании результат сравнивается равным исходному указателю.(Как и в случае с объектами, вам разрешено выполнять это преобразование, но использование результирующего указателя для вызова несовместимой функции имеет неопределенное поведение.)

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

0 голосов
/ 19 сентября 2018

Когда типы T1 и T2 различны, назначения между T1 *p1 и T2 *p2 формально запрещены, если хотя бы один из T1 и T2 не равен void, а другой не является объектом(не функциональный) тип.

Во многих случаях несовместимые назначения будут работать на практике, особенно на машинах (таких как все популярные сегодня) с «плоскими» адресными пространствами, и где все типы указателей имеют одно и то же внутреннее представление.

Однако после назначения указателя «смешанного режима» могут возникнуть проблемы с разыменованием указателей из-за (1) проблем с выравниванием и (2) строгого алиасинга .

Поскольку назначения указателей в «смешанном режиме» формально недопустимы и часто являются плохой идеей, большинство компиляторов предупреждают о них.Большинство компиляторов позволяют подавлять предупреждения явным приведением.Большую часть времени актеры служат только для подавления предупреждения;это не вводит никакого фактического преобразования, которое не было бы выполнено так или иначе.(То есть изменение p1 = p2 на p1 = (T1 *)p2 очень похоже на изменение i = f на i = (int)f, где i - это целое число, а f - это число с плавающей точкой.)


Приложение: Я написал: «Когда типы T1 и T2 различны», но более точное утверждение было бы, когда они несовместимы .Например, типы char и unsigned char совместимы, поэтому назначение указателей этих типов в порядке.См. Более длинный ответ Эрика Постпишила для получения более подробной информации.

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