Подтвердить что ты не робот

Каков синтаксически правильный способ объявления 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;
}
4b9b3361

Ответ 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;

//========================================================