Является ли & * p допустимым C, учитывая, что p является указателем на неполный тип? - PullRequest
25 голосов
/ 04 августа 2011

Является ли следующий пример допустимой полной единицей перевода в C?

struct foo;

struct foo *bar(struct foo *j)
{
    return &*j;
}

struct foo является неполным типом, но я не могу найти явный запрет разыменования неполного типа в стандарте C.В частности, §6.5.3.2 гласит:

Унарный оператор & возвращает адрес своего операнда.Если операнд имеет тип '' type '', результат имеет тип '' указатель на тип ''.Если операнд является результатом унарного оператора *, ни этот оператор, ни оператор & не оцениваются, и результат такой, как если бы оба опущены, за исключением того, что ограничения на операторы все еще применяются, и результат не являетсяlvalue.

Тот факт, что результат не является lvalue, не имеет значения - возвращаемые значения не должны быть.На оператор * накладываются следующие ограничения:

Операнд унарного оператора * должен иметь тип указателя.

, а на оператор &:

Операндом унарного оператора & должен быть либо указатель функции, результат [] или унарный оператор *, либо lvalue, который обозначает объект, который не являетсябитовое поле и не объявляется с помощью register спецификатора класса хранения.

Оба из них здесь тривиально выполняются, поэтому результат должен быть эквивалентен просто return j;.

Однако gcc 4.4.5 не компилирует этот код.Вместо этого выдается следующая ошибка:

y.c:5: error: dereferencing pointer to incomplete type

Это дефект в gcc?

Ответы [ 3 ]

6 голосов
/ 04 августа 2011

Да, я думаю, что это ошибка. Даже значения l неполных типов, поэтому *j, по-видимому, разрешены в зависимости от контекста:

6.3.2.1 ... lvalue - это выражение с типом объекта или неполный тип кроме void

В основном это должно работать, если вы ничего не делаете с таким значением, которое нужно знать о структуре struct. Поэтому, если вы не обращаетесь к объекту или не спрашиваете о его размере, это законно.

3 голосов
/ 04 августа 2011

Стандарт C99 (ISO / IEC 9899: 1999) описывает поведение:

§6.5.3.2 Операторы адреса и косвенности

Унарный оператор & возвращает адрес своегооперанд.Если операнд имеет тип '' type '', результат имеет тип '' указатель на тип ''.Если операнд является результатом унарного оператора *, ни этот оператор, ни оператор & не оцениваются, и результат, как если бы оба были опущены, за исключением того, что ограничения на операторы все еще применяются, и результат не является lvalue.

Это означает, что &*j эквивалентно j.

Однако, j должен быть указателем на объект, и это только указатель на неполноеtype, как говорится в GCC 4.4.5.

§6.3.2.3 Указатели

Указатель на void может быть преобразован в указатель или из указателя на любой неполный или объектный тип.Указатель на любой неполный или тип объекта может быть преобразован в указатель на void и обратно;результат должен сравниваться равным исходному указателю.

Обратите внимание, что он различает тип объекта и неполный тип;это часто встречается в стандарте.

Итак, это наблюдение в вопросе неверно:

Оба из которых здесь тривиально удовлетворяются,

переменная j не является указателем на объект;это указатель на неполный тип, который не является объектом.


§6.2.5 Типы

[...] Типы подразделяются на типы объектов (типы, которые полностью описывают объекты), типы функций (типы, которые описывают функции) и неполные типы (типы, которые описывают объекты, но не имеют информации, необходимой для определения их размеров).

2 голосов
/ 04 августа 2011

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

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

Причина, по которой gcc 4.4.5 жалуется, заключается в следующем: если вы используете указатели на неполный тип вне реализации, он должен работать. Но код является частью реализации, и здесь вы, вероятно, хотите иметь полный тип.

...