Каковы различия между этими двумя стилями определения типа в C?

Мне любопытно, что различие здесь когда typedefing перечисление или структура. Есть ли какое-либо различие семантически между этими двумя блоками?

Это:

typedef enum { first, second, third } SomeEnum;

и это:

enum SomeEnum { first, second, third };
typedef enum SomeEnum SomeEnum;

То же соглашение для структур. Я видел и используемый, и они оба, кажется, делают то же самое в C или Objective C. Существует ли реальная разница, или это - просто предпочтение, для которого стиля можно использовать?

12
задан Kevlar 6 July 2010 в 21:32
поделиться

6 ответов

Разница в том, что второй подход объявляет тип с именем enum SomeEnum , а также объявляет имя typedef SomeEnum - псевдоним для этого типа. Фактически его можно объединить в эквивалентную однострочную

typedef enum SomeEnum { first, second, third } SomeEnum;

, что делает довольно очевидным, что единственное различие между двумя подходами состоит в том, есть ли имя после ключевого слова enum . При втором подходе вы можете объявить объект этого типа перечисления, используя либо SomeEnum e , либо enum SomeEnum e , в зависимости от того, что вы предпочитаете.

Первый подход объявляет только typedef-name SomeEnum для первоначально анонимного типа перечисления, что означает, что вы ограничены объявлениями SomeEnum e .

Итак, пока вы используете только typedef-name SomeEnum в своих объявлениях, между ними не будет никакой разницы. Однако в некоторых случаях вам может потребоваться использовать полное исходное имя типа enum SomeEnum . В первом подходе это имя недоступно, поэтому вам не повезет.

Например, если после вышеуказанного объявления вы также объявляете переменную с именем SomeEnum в некоторой вложенной области

int SomeEnum;

, имя переменной скроет typedef-name перечисления, тем самым сделав это объявление незаконно

SomeEnum e; /* ERROR: `SomeEnum` is not a type */

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

enum SomeEnum e; /* OK */

. Это было бы невозможно, если бы вы использовали первый подход при объявлении вашего перечисляемого типа.

При использовании со структурами имя после struct является обязательным, когда вам нужен самореферентный тип (тип, содержащий указатель на тот же тип), например

typedef struct SomeStruct {
  struct SomeStruct *next;
} SomeStruct;

Наконец, в при втором подходе имя typedef совершенно необязательно. Вы можете просто объявить

enum SomeEnum { first, second, third };

и просто использовать enum SomeEnum каждый раз, когда вам нужно обратиться к этому типу.

15
ответ дан 2 December 2019 в 05:26
поделиться

Первая форма создает анонимный тип enum и псевдоним SomeEnum для него.

Вторая форма создает как тип enum SomeEnum, так и псевдоним SomeEnum для него.

(В языке Си для типов существуют отдельные пространства имен. То есть, struct Foo отличается от enum Foo, который отличается от Foo.)

Это более важно для structs, чем enums, поскольку вам придется использовать вторую форму, если ваш struct будет самореферентным. Например:

struct LinkedListNode
{
    void* item;
    struct LinkedListNode* next;
};

typedef struct LinkedListNode LinkedListNode;

Приведенное выше невозможно при использовании первой формы.

2
ответ дан 2 December 2019 в 05:26
поделиться

Да, есть семантическая разница. Второй фрагмент объявляет идентификатор тега, а первый — нет. Оба объявляют обычный идентификатор.

Это означает, что для первого этот код недействителен, но для второго он действителен:

enum SomeEnum foo;

Насколько я знаю, никакой другой семантической разницы между ними в вашем коде нет. Для структур и союзов вторая форма, возможно, в сочетании с typedef в одном объявлении, необходима для рекурсивных типов

typedef struct node {
  struct node *parent; // refer to the tag identifier
} node;

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

Помимо отделения идентификаторов, которые ссылаются на структуры, союзы и перечисления, от идентификаторов, которые ссылаются на значения, идентификаторы тегов также полезны для создания имен forward:

/* forward declaration */
struct foo; 

/* for pointers, forward declarations are entirely sufficient */
struct foo *pfoo = ...;

/* ... and then later define its contents */
struct foo {
  /* ... */
};

Имена Typedef не могут быть объявлены повторно в одной и той же области (в отличие от C++, где они могут), и они должны ссылаться на существующий тип, чтобы их нельзя было использовать для создания объявлений пересылки.

7
ответ дан 2 December 2019 в 05:26
поделиться

Единственная реальная разница в том, что во втором случае вы можете использовать что-то вроде:

enum SomeEnum x;

, тогда как первый только поддерживает:

SomeEnum x;

Людям, у которых есть долгое время писал C, определяя struct без ключевого слова struct , часто "кажется" странным ...

4
ответ дан 2 December 2019 в 05:26
поделиться

Для structов есть реальная разница, которая не сводится только к именованию.

Это правильный C:

struct SomeEnum { struct SomeEnum *first; };

Это не:

typedef struct { SomeEnum *first; } SomeEnum;
0
ответ дан 2 December 2019 в 05:26
поделиться

Добавляя к комментарию user207442, модуль исходного кода может объявлять переменные типа "struct foo *" без ever, имеющего определение структуры. Такой модуль не сможет разымещать такие указатели, но может передавать их другим модулям и из них.

Например, в заголовочном файле можно определить тип "USERCONSOLE", используя "typedef struct _USERCONSOLE *USERCONSOLE;". Код, который #include этот заголовочный файл, может иметь переменные типа USERCONSOLE и передавать такие переменные в/из модулей, которые знают, что такое _USERCONSOLE на самом деле, без того, чтобы заголовочный файл раскрывал фактическое определение структуры.

0
ответ дан 2 December 2019 в 05:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: