Почему указатели на неопределенные структуры иногда являются незаконными в C и C

#c #c #pointers

#c #c #указатели

Вопрос:

Почему

 void foo(T*);
  

недопустимы как в C, так и в C (потому что T не определено), тогда как

 void foo(struct T*);
  

допустимо, даже если оно по-прежнему не определяет T ? Существует ли какая-либо ситуация, в которой для вызывающего объекта имеет смысловое значение, является ли T это struct или какой-либо другой тип (class / enum / typedef / etc.)?

Комментарии:

1. просто из любопытства, зачем вам нужно брать указатель на неопределенную структуру? Если это не определено, то почему бы просто не использовать void * ?

2. @John: Потому что я еще не включил соответствующий заголовочный файл для определения T , но я все еще хочу, чтобы мой код компилировался, потому что я не понимаю, почему это имеет значение, что T есть.

3. @John: Это обычное дело. Зачем вводить [потенциально тяжелое] определение, когда все, что вам нужно [на этом этапе вашего кода], — это объявление?

Ответ №1:

void foo(struct T*); одновременно функционирует как прямое объявление struct T в пределах этой области, поэтому можно безопасно присвоить ему указатель.

В void foo(T*) невозможно сказать, каким T должно быть. Если оказывается, что это имя переменной, а не типа, то строка кода неправильно сформирована. C требуется больше информации, прежде чем он сможет сказать, что строка кода допустима.

Комментарии:

1. @Doug: Интересно о прямом объявлении. Но почему это делает то, что T есть, когда все, что нам нужно знать, это то, что мы принимаем указатель?

2. @Mehrdad: Потому что, если T это не тип (и, помните, «мы» не знаем, что это такое), то строка кода неправильно сформирована. [Я добавил это к своему ответу.]

3. @Tomalak: Так это просто для того, чтобы сделать грамматику более строгой, а не по каким-либо причинам реализации / двусмысленности?

4. @Mehrdad: По существу. Я бы сказал, что все три перечисленных вами фактора по своей сути взаимосвязаны. 🙂

5. @Mehrdad: чомп сказал это лучше всего в своем ответе: «Если бы простое появление ранее невидимого символа семантически подразумевало прямое объявление типа, я думаю, у нас было бы много проблем».

Ответ №2:

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

6.2.5/27 (стандарт C99)

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


С другой стороны, указатели на что-либо могут иметь различные свойства ширины и / или выравнивания (void*, указатели на функции, unsigned char *, …)

Комментарии:

1. @pmg: Я не могу представить, почему указатель на объединение может, например, когда-либо отличаться от указателя на структуру, но если это гипотетически может быть так, то это имеет смысл. 🙂 Спасибо!

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

3. @Tomolak: Совсем наоборот. Этот ответ является единственным ответом на вопрос.

4. @Tomalak: Дело в том, что моя проблема заключалась не в том, «Почему мой код не компилируется?» но «Почему C / C были разработаны таким образом?». В вашем ответе указывается тот факт, что он не компилируется, и указывается причина, по которой, но в нем не говорится, в чем была бы проблема, если бы ограничение было снято — этот ответ, с другой стороны, объясняет проблему: не все указатели могут быть одинаковыми на данной платформе, поэтому компилятор просто не может сгенерировать корректный код. Сейчас я просто пытаюсь понять, мог ли компилятор притвориться, что это void* , но ответ иначе объясняет основную причину проблемы.

5. @Tomalak: Я не уверен, к чему вы пытаетесь подойти, но лично я нашел этот ответ более убедительным, потому что он объяснял первопричину, а не просто говорил «так оно и есть». Если кто-то другой находит ваши более убедительными, тогда отлично! Вот почему мы можем разместить несколько ответов на этом сайте. :]

Ответ №3:

 struct T
  

действует как прямое объявление T. Вы обещаете компилятору, что определите это позже. Если бы вы действительно что-то сделали на T или попытались создать его экземпляр, у вас возникли бы проблемы:

   struct T t1; //error, incomplete type
  struct T* t2; // ok
  t2->foo     // error, incomplete type
  

Просто

  T
  

является произвольным идентификатором. Это переменная? Это функция? Язык не предоставляет возможности для прямого объявления без добавления struct или class перед переменной.

Комментарии:

1. Интересно. Но почему это делает то, что T есть, когда все, что нам нужно знать, это то, что мы принимаем указатель?

Ответ №4:

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

Без struct компилятор ничего не знает о том, что T должно быть.

Если T они ранее были объявлены как a, то struct это void foo(T*) было бы разрешено в C , но не в C, потому что имена struct s автоматически не становятся именами типов, хотя вы можете объявить a typedef с идентичным именем в C, если хотите.

Комментарии:

1. Да, но мой вопрос в том, почему это имеет значение ? т.Е. какая разница, что T есть, когда все, что нам нужно знать, это то, что мы принимаем указатель?

2. @Mehrdad: Я не уверен, что понимаю, к чему вы клоните. Таковы правила языка. Любой идентификатор — объект, функция или тип — должен быть объявлен перед его использованием, и struct использует T объявление.

3. Потому что все указатели на структуры (любые структуры) имеют одинаковые свойства ширины и выравнивания (6.2.5 / 27 "All pointers to structure types shall have the same representation and alignment requirements as each other." ). С другой стороны, указатели на что-либо могут иметь различные свойства ширины и / или выравнивания (void*, указатели на функции, unsigned char *, …)

4. @pmg: Таким образом, указатель на структуру и указатель на объединение / перечисление / и т.д. Могут иметь разные свойства выравнивания?