#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: Таким образом, указатель на структуру и указатель на объединение / перечисление / и т.д. Могут иметь разные свойства выравнивания?