Странное предупреждение компилятора C: предупреждение: 'struct' объявлена внутри списка параметров
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:025 плюса
Это связано с тем, что в первом примере структура ранее не была определена, и поэтому компилятор пытается обработать эту первую ссылку на эту структуру как определение.
В общем, C - это язык, где порядок ваших объявлений имеет значение. Все, что вы используете, должно быть должным образом объявлено заранее в некоторой степени, чтобы компилятор мог рассуждать об этом, когда на него ссылаются в другом контексте.
Это не ошибка или ошибка в дизайне языка. Скорее, это выбор, который, я считаю, был сделан, чтобы упростить реализации первых компиляторов Си. Прямые объявления позволяют компилятору переводить исходный код последовательно за один проход (при условии, что известна некоторая информация, такая как размеры и смещения). Если бы это было не так, компилятор мог бы перемещаться назад и вперед в программе всякий раз, когда он встречает нераспознанный идентификатор, требуя, чтобы его цикл передачи кода был намного более сложным.
Автор: Theodoros Chatzigiannakis Размещён: 30.05.2013 08:474 плюса
Компилятор предупреждает вас о прямом объявлении о 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
создать функцию для перехода к функции) и почти наверняка не соответствует замыслу программиста, поэтому компилятор предупреждает.
Вопросы из категории :
- c Как вы форматируете unsigned long long int, используя printf?
- c What are the barriers to understanding pointers and what can be done to overcome them?
- c Как реализовать продолжения?
- c Как вы передаете функцию в качестве параметра в C?
- c Как получить список каталогов в C?
- c В чем разница между #include <filename> и #include "filename"?
- c Всегда ли выгодно использовать «goto» в языке, который поддерживает циклы и функции? Если так, то почему?
- c В чем разница между ++ i и i ++?
- c Есть ли разница в производительности между i ++ и ++ i в C?
- c Какой самый лучший бесплатный детектор утечки памяти для программы на C / C ++ и ее подключаемых библиотек DLL?
- c Как разделить два 64-битных числа в ядре Linux?
- c Как определить размер моего массива в C?
- c Как я могу запустить внешнюю программу из C и проанализировать ее вывод?
- c Как вы обнаруживаете / избегаете утечки памяти в вашем (неуправляемом) коде?
- c Как вы устанавливаете, очищаете и переключаете один бит?
- c Преобразование без знака в C - всегда ли это безопасно?
- c Что делает оператор запятой?
- c Как начать потоки в plain C?
- c How to determine CPU and memory consumption from inside a process?
- c Что такое неподписанный символ?