2010-07-06 2 views
13

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

Это:

typedef enum { first, second, third } SomeEnum; 

и это:

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

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

ответ

15

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

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

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

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

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

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

int SomeEnum; 

имя переменной будет скрыть имя-ЬурейеЕ из перечисления, таким образом, делая это заявление незаконным

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 каждый раз, когда вам нужно обратиться к этому типу.

7

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

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

enum SomeEnum foo; 

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

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

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

Кроме разделения идентификаторов, которые относятся к структурам, объединениям и перечислениям от тех, которые относятся к значениям, идентификаторы тегов могут быть также использованы для создания вперед декларации:

/* forward declaration */ 
struct foo; 

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

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

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

4

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

enum SomeEnum x; 

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

SomeEnum x; 

Для людей, которые уже писал C долгое время, определяя struct без ключевого слова struct часто «чувствует» странно ...

2

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

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

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

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

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

typedef struct LinkedListNode LinkedListNode; 

Вышеуказанное не было бы возможно с первой формой.

0

Для struct существует реальная разница, а не просто именование.

Это действительно C:

struct SomeEnum { struct SomeEnum *first; }; 

Это не так:

typedef struct { SomeEnum *first; } SomeEnum; 
0

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

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

Смежные вопросы