Каков синтаксически правильный способ объявления C-структуры?
Я видел, как C structs объявляли несколько разных способов раньше. Почему это и что, если что-нибудь, каждый делает разные?
Например:
struct foo {
short a;
int b;
float c;
};
typedef struct {
short d;
int e;
float f;
} bar;
typedef struct _baz {
short a;
int b;
float c;
} baz;
int main (int argc, char const *argv[])
{
struct foo a;
bar b;
baz c;
return 0;
}
Ответы
Ответ 1
Хорошо, очевидная разница проявляется в вашем main
:
struct foo a;
bar b;
baz c;
Первое объявление имеет un typedef
ed struct
и нуждается в ключевом слове struct
. Второй имеет typedef
ed анонимный struct
, поэтому мы используем имя typedef
. Третий объединяет как первый, так и второй: в вашем примере используется baz
(что удобно коротко), но можно так же легко использовать struct _baz
для того же эффекта.
Обновление: ответ larsmans упоминает более распространенный случай, когда вы должны использовать не менее struct x { }
для создания связанного списка. Второй случай здесь невозможен (если вы не откажетесь от здравого смысла и вместо этого используйте void *
), потому что struct
является анонимным, а typedef
не выполняется до тех пор, пока не будет определен struct
, что не даст вам способ сделать (тип-безопасный) указатель на тип struct
. Первая версия отлично подходит для этого использования, но третий, как правило, предпочитается в моем опыте. Дайте ему некоторую репутацию для этого.
Более тонкая разница заключается в размещении пространства имен. В C теги struct
помещаются в отдельное пространство имен из других имен, но имена typedef
не являются. Итак, законно:
struct test {
// contents
};
struct test *test() {
// contents
}
Но это не так, потому что было бы двусмысленно, что такое имя test
:
typedef struct {
// contents
} test;
test *test() {
// contents
}
typedef
делает имя короче (всегда плюс), но оно помещает его в то же пространство имен, что и ваши переменные и функции. Обычно это не проблема, но это тонкая разница, помимо простого сокращения.
Ответ 2
Это во многом вопрос личных предпочтений. Я хотел бы дать новым типам имя, начинающееся с прописной буквы, и опустить struct
, поэтому я обычно пишу typedef struct { ... } Foo
. Это значит, что я не могу написать struct Foo
.
Исключением является, когда a struct
содержит указатель на свой собственный тип, например
typedef struct Node {
// ...
struct Node *next;
} Node;
В этом случае вам также нужно объявить тип struct Node
, так как typedef
не входит в область определения struct
. Обратите внимание, что оба имени могут быть одинаковыми (я не уверен, что было принято соглашение о подчеркивании, но я думаю, что более старые компиляторы C не могли обрабатывать typedef struct X X;
).
Ответ 3
Все ваши применения синтаксически правильны. Я предпочитаю следующее использование
/* forward declare all structs and typedefs */
typedef struct foo foo;
.
.
/* declare the struct itself */
struct foo {
short a;
int b;
foo* next;
};
Обратите внимание, что это легко позволяет использовать typedef
уже внутри объявления самого struct
и что даже для struct
, которые ссылаются друг на друга взаимно.
Ответ 4
Путаница возникает из-за того, что некоторые из объявлений фактически объявляют до трех конструкций C. Вы должны иметь в виду разницу между: 1. Объявление typedef, 2. Определение структуры и 3. Объявление структуры.
Все они очень разные конструкции C. Все они делают разные вещи; но вы можете объединить их в одну составную конструкцию, если хотите.
Посмотрите на каждое объявление по очереди.
//================================================
struct foo {
short a;
int b;
float c;
};
Здесь мы используем самый базовый синтаксис определения структуры. Мы определяем foo как тип C, который впоследствии может быть использован для объявления переменных этого типа с использованием следующего синтаксиса:
foo myFoo; // Declare a struct variable of type foo.
//============================================= =======
Это следующее объявление немного похоже на предыдущий синтаксис, поскольку он объявляет тип C, но использует синтаксис typedef. Позвольте разбить его на свои компоненты, используя предыдущую базовую декларацию.
typedef foo bar; // Declare bar as a variable type, the alias of foo.
bar myBar; // This is ssemantically the same as: foo myBar;
Теперь просто замените "foo" на предыдущий синтаксис и voila!
typedef struct {
short d;
int e;
float f;
} bar;
//==================================================================
typedef struct _baz {
short a;
int b;
float c;
} baz;
Вышеупомянутый синтаксис эквивалентен следующей последовательности объявлений.
struct _baz {
short a;
int b;
float c;
}
typedef _baz baz; // Declare baz as an alias for _baz.
baz myBaz; // Is the same as: _baz myBaz;
//========================================================