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

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

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

Это:

typedef enum { first, second, third } SomeEnum;

и это:

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

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

4b9b3361

Ответ 1

Разница в том, что второй подход объявляет тип с именем 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;

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

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

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

enum SomeEnum e; /* OK */

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

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

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

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

enum SomeEnum { first, second, third };

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

Ответ 2

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

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

enum SomeEnum foo;

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

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

Обычный идентификатор еще не виден в спецификаторе структуры, поэтому вам нужно обратиться к структуре уже объявленным идентификатором тега. Идентификаторы тегов ссылаются, добавляя их "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 не могут быть объявлены повторно в той же области (в отличие от С++, где они могут), и им нужно ссылаться на существующий тип, чтобы они не могли использоваться для создания форвардных объявлений.

Ответ 3

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

enum SomeEnum x;

тогда как первая поддерживает только:

SomeEnum x;

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

Ответ 4

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

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

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

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

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

typedef struct LinkedListNode LinkedListNode;

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

Ответ 5

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

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

struct SomeEnum { struct SomeEnum *first; };

Это не:

typedef struct { SomeEnum *first; } SomeEnum;

Ответ 6

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

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