typedef struct vs struct definitions

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

struct myStruct{ int one; int two; }; 

против

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

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

 typedef struct X { int x; } X; 

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

 struct S { int x; }; typedef struct SS; 

В первой строке вы определяете идентификатор S в пространстве имен структуры (не в смысле C ++). Вы можете использовать его и определить переменные или аргументы функции только что определенного типа, указав тип аргумента как 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 ++ это немного отличается, поскольку правила поиска символа изменились тонко. C ++ все еще сохраняет два разных пространства идентификаторов, но в отличие от C, когда вы определяете только символ в пространстве идентификаторов classа, вам не требуется указывать ключевое слово struct / class:

  // C++ struct S { int x; }; // S defined as a class void f( S a ); // correct: struct is optional 

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

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

 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; } 

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

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

 struct foo { int n; }; 

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

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 по имени, то он не является непрозрачным, это явно структура, и, на мой взгляд, имеет смысл ссылаться на нее как на структуру. Но многие умные программисты не согласны со мной по этому вопросу. Будьте готовы читать и понимать код, написанный в любом случае.)

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

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

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

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

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

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

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

 struct myStruct myVariable; 

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

 typedef struct myStruct someStruct; someStruct myVariable; 

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

 typedef struct { ... } myStruct; 

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

 struct mystruct myvar; 

Нельзя писать

 mystruct myvar; 

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

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

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

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

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

Разница возникает при использовании struct .

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

 struct myStruct aName; 

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

 myStruct aName; 

Вы не можете использовать форвардную декларацию с помощью 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 

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

 struct myStruct blah; 

против

 myStruct blah; 

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

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

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

Я вижу, что в этом есть некоторые разъяснения. C и C ++ не определяют типы по-разному. C ++ первоначально был не чем иным, как дополнительным набором включений поверх 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; 

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

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

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

 myStruct a; 

вместо

 struct myStruct a; 

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

  • Статический polymorphism C ++ (CRTP) и использование typedefs из производных classов
  • typedef указатель const weirdness
  • Понимание typedefs для указателей функций в C
  • Указатель функции Typedef?
  • Почему мы должны набирать структуру так часто в C?
  • C typedef указателя на структуру
  • Переслать-объявить перечисление в Objective-C
  • uint8_t против символа без знака
  • Почему «typdef struct {struct S * s; } S; ", содержащий указатель на компиляцию того же типа?
  • Почему мне нужно использовать typedef typename в g ++, но не VS?
  • Как определить структуру typedef, содержащую указатели на себя?
  • Interesting Posts

    Бесплатный конвертер изображений в командной строке

    Можем ли мы заменить память и жесткий диск для ноутбуков – Compaq Presario v2000?

    Привязка OneWayToSource от свойства readonly в XAML

    Бесплатная FTP-библиотека

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

    Звук не работает в iPhone Simulator?

    Сравнение CMake с пустой строкой с ошибкой STREQUAL

    Как печатать двойной с двумя десятичными знаками в Android?

    Настройка маршрутизатора для перенаправления запросов с локальной машины

    Проверьте, существует ли таблица без использования «select from»

    Почему непривилегированный пользователь не может изменять права собственности на файл?

    Неверная стартовая папка в программах Gnome под KDE

    Как преобразовать .iso в .mp4 без установки с помощью ffmpeg

    Как сохранить фотографии из Firefox без повторной загрузки

    Динамическая кнопка asp.net с обработчиком событий

    Давайте будем гением компьютера.