Определение typedef struct vs struct

Я новичок в программировании на С, но мне было интересно узнать, какая разница между использованием typedef при определении структуры или использованием typedef. Мне кажется, что нет никакой разницы, они достигают той же цели.

struct myStruct{
    int one;
    int two;
};

против

typedef struct{
    int one;
    int two;
}myStruct;

Ответ 1

Общая идиома использует оба:

typedef struct X { 
    int x; 
} X;

Это разные определения. Чтобы сделать обсуждение более ясным, я разделил предложение:

struct S { 
    int x; 
};

typedef struct S S;

В первой строке вы определяете идентификатор S в пространстве имен структуры (не в смысле С++). Вы можете использовать его и определить переменные или аргументы функции только что определенного типа, указав тип аргумента как struct S:

void f( struct S argument ); // struct is required here

Вторая строка добавляет псевдоним типа S в глобальное пространство имен и, таким образом, позволяет вам просто написать:

void f( S argument ); // struct keyword no longer needed

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

Чтобы сделать разницу более четкой:

typedef struct S { 
    int x; 
} T;

void S() { } // correct

//void T() {} // error: symbol T already defined as an alias to 'struct S'

Вы можете определить функцию с тем же именем структуры, что и идентификаторы, хранятся в разных пространствах, но вы не можете определить функцию с тем же именем, что и typedef, поскольку эти идентификаторы сталкиваются.

В С++ он немного отличается, поскольку правила поиска символа изменились тонко. С++ все еще сохраняет два разных пространства идентификаторов, но в отличие от C, когда вы определяете только символ в пространстве идентификаторов класса, вам не требуется указывать ключевое слово struct/class:

 // C++
struct S { 
    int x; 
}; // S defined as a class

void f( S a ); // correct: struct is optional

Какие изменения являются правилами поиска, а не там, где определены идентификаторы. Компилятор будет искать таблицу глобального идентификатора и после того, как S не будет найден, он будет искать S внутри идентификаторов класса.

Представленный выше код ведет себя одинаково:

typedef struct S { 
    int x; 
} T;

void S() {} // correct [*]

//void T() {} // error: symbol T already defined as an alias to 'struct S'

После определения функции S во второй строке структура S не может быть автоматически разрешена компилятором, а для создания объекта или определения аргумента этого типа вы должны вернуться к включению struct ключевое слово:

// previous code here...
int main() {
    S(); 
    struct S s;
}

Ответ 2

struct и typedef - две разные вещи.

Ключевое слово struct используется для определения или ссылки на тип структуры. Например, это:

struct foo {
    int n;
};

создает новый тип, называемый struct foo. Имя foo - это тег; он имеет смысл только тогда, когда ему сразу предшествует ключевое слово struct, потому что теги и другие идентификаторы находятся в разных пространствах имен. (Это похоже на, но гораздо более ограниченное, чем концепция С++ namespace s.)

A typedef, несмотря на имя, не определяет новый тип; он просто создает новое имя для существующего типа. Например, данный:

typedef int my_int;

my_int - новое имя для int; my_int и int являются точно такими же. Аналогично, учитывая определение struct выше, вы можете написать:

typedef struct foo foo;

Тип уже имеет имя struct foo. Объявление typedef дает тот же тип новому имени, foo.

Синтаксис позволяет объединить struct и typedef в одно объявление:

typedef struct bar {
    int n;
} bar;

Это обычная идиома. Теперь вы можете ссылаться на этот тип структуры либо как struct bar, либо как bar.

Обратите внимание, что имя typedef не становится видимым до конца объявления. Если структура содержит указатель на себя, вы используете версию struct для ссылки на нее:

typedef struct node {
    int data;
    struct node *next; /* can't use just "node *next" here */
} node;

Некоторые программисты будут использовать разные идентификаторы для тега struct и для имени typedef. На мой взгляд, нет веских оснований для этого; используя одно и то же имя, является совершенно законным и дает четкое представление о том, что они одного типа. Если вы должны использовать разные идентификаторы, по крайней мере используйте согласованное соглашение:

typedef struct node_s {
    /* ... */
} node;

(Лично я предпочитаю пропускать typedef и ссылаюсь на тип как struct bar. typedef сохраняет немного ввода, но скрывает тот факт, что это тип структуры. Если вы хотите, чтобы тип быть непрозрачным, это может быть хорошо. Если код клиента будет ссылаться на член n по имени, то он не непрозрачен, это явно структура, и, на мой взгляд, имеет смысл ссылаться на нее как на но многие умные программисты не согласны со мной в этом вопросе. Будьте готовы читать и понимать код, написанный в любом случае.)

(С++ имеет разные правила. Учитывая объявление struct blah, вы можете ссылаться на тип как на blah, даже без typedef. Использование typedef может сделать ваш C-код немного более похожим на С++ - если вы думаете, что это хорошо.)

Ответ 3

Другое отличие, которое не указано, заключается в том, что предоставление структуры имени (т.е. struct myStruct) также позволяет вам предоставлять декларации вперед структуры. Поэтому в другом файле вы можете написать:

struct myStruct;
void doit(struct myStruct *ptr);

не имея доступа к определению. Я рекомендую вам объединить два примера:

typedef struct myStruct{
    int one;
    int two;
} myStruct;

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

Ответ 4

В C (не С++) вы должны объявить структурные переменные, например:

struct myStruct myVariable;

Чтобы использовать myStruct myVariable; вместо этого, вы можете typedef struct:

typedef struct myStruct someStruct;
someStruct myVariable;

Вы можете объединить определение struct и typedef его в один оператор, объявляющий анонимные struct и typedef его.

typedef struct { ... } myStruct;

Ответ 5

Если вы используете struct без typedef, вам всегда придется писать

struct mystruct myvar;

Нелегально писать

mystruct myvar;

Если вы используете typedef, вам больше не нужен префикс struct.

Ответ 6

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

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

Поэтому рекомендуется (см., например, руководство по стилю ядра ядра Linux, глава 5), чтобы это делать только тогда, когда вы действительно хотите скрыть эту информацию, а не просто сохранить несколько нажатий клавиш.

Примером использования typedef будет непрозрачный тип, который используется только с соответствующими функциями/макросами доступа.

Ответ 7

Разница возникает, когда вы используете struct.

Первый способ, которым вы должны выполнить:

struct myStruct aName;

Второй способ позволяет удалить ключевое слово struct.

myStruct aName;

Ответ 8

Вы не можете использовать форвардное объявление с typedef struct.

Сам struct является анонимным типом, поэтому у вас нет фактического имени для переадресации объявления.

typedef struct{
    int one;
    int two;
} myStruct;

Непосредственное объявление вроде этого не будет работать:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types

Ответ 9

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

struct myStruct blah;

против.

myStruct blah;

Ответ 10

Следующий код создает анонимную структуру с псевдонимом myStruct:

typedef struct{
    int one;
    int two;
} myStruct;

Вы не можете ссылаться на него без псевдонима, потому что вы не указываете идентификатор структуры.

Ответ 11

Я вижу, что в этом есть некоторые разъяснения. C и С++ не определяют типы по-разному. С++ первоначально был не чем иным, как дополнительным набором включений поверх C.

Проблема, с которой сегодня сталкиваются практически все разработчики C/С++: а) университеты больше не учат основам, а б) люди не понимают разницу между определением и декларацией.

Единственная причина, по которой такие объявления и определения существуют, заключается в том, что компоновщик может вычислять смещения адресов для полей в структуре. Вот почему большинство людей уходит с кодом, который на самом деле написан неправильно - потому что компилятор способен определить адресацию. Проблема возникает, когда кто-то пытается сделать что-то заранее, например, в очереди или связанном списке, или копируя структуру O/S.

Объявление начинается с 'struct', определение начинается с 'typedef'.

Кроме того, структура имеет знак прямого декларирования и определенную метку. Большинство людей этого не знают и используют ярлык прямого объявления как метку define.

Неправильно:

struct myStruct
   {
   int field_1;
   ...
   };

Они просто использовали объявление forward для маркировки структуры - так что теперь компилятор знает об этом, но он не является определенным определенным типом. Компилятор может вычислить адресацию - но это не то, как оно предназначалось для использования, по причинам, которые я покажу на мгновение.

Люди, которые используют эту форму декларации, всегда должны всегда "связывать" структуру с каждой ссылкой на нее, потому что это не является официальным новым типом.

Вместо этого любая структура, которая не ссылается на себя, должна быть объявлена ​​и определена только так:

typedef struct
   {
   field_1;
   ...
   }myStruct;

Теперь это фактический тип, и при использовании вы можете использовать его как "myStruct", не добавляя его со словом "struct".

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

typedef struct
   {
   field_1;
   ...
   }myStruct,*myStructP;

Теперь у вас есть указательная переменная для этой структуры, настраиваемая для нее.

ПЕРЕДНЯЯ ДЕКЛАРАЦИЯ -

Теперь, вот фантастический материал, как работает форвардная декларация. Если вы хотите создать тип, который ссылается на себя, например связанный список или элемент очереди, вам нужно использовать декларацию forward. Компилятор не рассматривает структуру, определенную до тех пор, пока она не дойдет до точки с запятой в самом конце, поэтому она просто объявлена ​​до этой точки.

typedef struct myStructElement
   {
   myStructElement*  nextSE;
   field_1;
   ...
   }myStruct;

Теперь, компилятор знает, что, хотя он еще не знает, что такое весь тип, он все равно может ссылаться на него с помощью прямой ссылки.

Пожалуйста, объявите и правильно напечатайте свои структуры. На самом деле есть причина.

Ответ 12

В последнем примере вы опускаете ключевое слово struct при использовании структуры. Поэтому везде в вашем коде вы можете написать:

myStruct a;

вместо

struct myStruct a;

Это сохранит некоторые типизации и может быть более читаемым, но это вопрос вкуса