Каковы различия между этими двумя стилями typedef в C?
Мне любопытно, какая разница здесь при наборе enum или struct. Есть ли разница между этими двумя блоками семантически?
Это:
typedef enum { first, second, third } SomeEnum;
и это:
enum SomeEnum { first, second, third };
typedef enum SomeEnum SomeEnum;
Такая же сделка для структур. Я видел, как они используются, и оба они, похоже, делают то же самое в C или Objective-C. Есть ли реальная разница или это просто предпочтение для стиля, который вы можете использовать?
Ответы
Ответ 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, без файла заголовка, который должен раскрывать фактическое определение структуры.