Странное предупреждение компилятора C: предупреждение: 'struct' объявлена ​​внутри списка параметров

c

27404 просмотра

3 ответа

Я только что нашел причуду в Си, которая меня очень смущает. В Си можно использовать указатель на структуру до того, как она была объявлена. Это очень полезная функция, которая имеет смысл, потому что объявление не имеет значения, когда вы просто имеете дело с указателем на него. Я только что нашел один угловой случай, когда это (на удивление) не соответствует действительности, и я не могу объяснить, почему. Для меня это выглядит как ошибка в дизайне языка.

Возьми этот код:

#include <stdio.h>

#include <stdlib.h>


typedef void (*a)(struct lol* etc);

void a2(struct lol* etc) {

}

int main(void) {
        return 0;
}

дает:

foo.c:6:26: warning: ‘struct lol’ declared inside parameter list [enabled by default]
foo.c:6:26: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default]
foo.c:8:16: warning: ‘struct lol’ declared inside parameter list [enabled by default]

Чтобы устранить эту проблему, мы можем просто сделать это:

#include <stdio.h>

#include <stdlib.h>

struct lol* wut;

typedef void (*a)(struct lol* etc);

void a2(struct lol* etc) {

}

int main(void) {
        return 0;
}

Необъяснимая проблема теперь исчезла по необъяснимой причине. Почему?

Обратите внимание, что этот вопрос касается поведения языка C (или, возможно, поведения компилятора gcc и clang), а не конкретного примера, который я вставил.

РЕДАКТИРОВАТЬ:

Я не приму «порядок объявления важен» в качестве ответа, если вы не объясните, почему C впервые предупреждает об использовании указателя структуры в списке аргументов функции, но разрешает это в любом другом контексте. Почему это может быть проблемой?

Автор: Hannes Landeholm Источник Размещён: 12.11.2019 09:25

Ответы (3)


30 плюса

Решение

Чтобы понять, почему компилятор жалуется, вам нужно знать две вещи о C "struct":

  • они создаются (как объявленный, но еще не определенный тип), как только вы их называете, поэтому самое первое вхождение struct lolсоздает объявление
  • они подчиняются тем же правилам "области объявления", что и обычные переменные

( struct lol {Декларирует , а затем начинается определение структуры, это struct lol;или struct lol *или что - то еще , что не имеет открывающую фигурную скобку , который останавливается после «объявляет» шага.)

Тип структуры, который объявлен, но еще не определен, является экземпляром того, что C называет «неполным типом». Вам разрешено использовать указатели на неполные типы, если вы не пытаетесь следовать указателю:

struct lol *global_p;
void f(void) {
    use0(global_p);     /* this is OK */
    use1(*global_p);       /* this is an error */
    use2(global_p->field); /* and so is this */
}

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

Однако в любом случае рассмотрим объявления функций с обычными intпараметрами:

int imin2(int a, int b); /* returns a or b, whichever is smaller */
int isum2(int a, int b); /* returns a + b */

Переменные с именами aи bздесь объявляются в скобках, но эти объявления должны быть удалены, чтобы следующее объявление функции не жаловалось на то, что они были повторно объявлены.

То же самое происходит с structименами тегов:

void gronk(struct sttag *p);

struct sttagОбъявляет структуру, а затем декларация сметена, как и те , для aи b. Но это создает большую проблему: тег исчез, и теперь вы не можете назвать тип структуры больше никогда! Если вы напишите:

struct sttag { int field1; char *field2; };

который определяет новое и другое struct sttag, как:

void somefunc(int x) { int y; ... }
int x, y;

определяет новый и другой xи yна уровне файлового уровня, отличный от тех, что в somefunc.

К счастью, если вы объявляете (или даже определяете) структуру до того, как напишите объявление функции, объявление уровня прототипа «отсылает назад» к объявлению внешней области видимости:

struct sttag;
void gronk(struct sttag *p);

Теперь оба struct sttag«одинаковы» struct sttag, поэтому, когда вы завершите struct sttagпозже, вы завершите тот же, что и в прототипе gronk.


Отредактируйте вопрос: конечно, было бы возможно определить действие тегов struct, union и enum по-разному, заставив их «пузыриться» от прототипов к их объемам. Это заставило бы проблему уйти. Но это не было определено таким образом. Поскольку именно комитет ANSI C89 изобрел (или украл, в действительности, из тогдашнего C ++) прототипы, вы можете винить их в этом. :-)

Автор: torek Размещён: 30.05.2013 09:02

5 плюса

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

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

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

Автор: Theodoros Chatzigiannakis Размещён: 30.05.2013 08:47

4 плюса

Компилятор предупреждает вас о прямом объявлении о struct lol. C позволяет вам сделать это:

struct lol;     /* forward declaration, the size and members of
                   struct lol are unknown */

Это чаще всего используется при определении структур со ссылками на себя, но также полезно при определении частных структур, которые никогда не определяются в заголовке. Из-за этого последнего варианта использования разрешено объявлять функции, которые получают или возвращают указатели на неполные структуры:

void foo(struct lol *x);

Однако простое использование необъявленной структуры в объявлении функции, как вы это сделали, будет интерпретироваться как локальное неполное объявление struct lol, область действия которого ограничена функцией. Эта интерпретация обязательна в стандарте C, но она бесполезна (нет способа struct lolсоздать функцию для перехода к функции) и почти наверняка не соответствует замыслу программиста, поэтому компилятор предупреждает.

Автор: user4815162342 Размещён: 30.05.2013 08:58
Вопросы из категории :
32x32