Почему мы должны набирать структуру так часто в C?

Я видел много программ, состоящих из таких структур, как:

typedef struct { int i; char k; } elem; elem user; 

Почему так часто требуется? Любая конкретная причина или применимая область?

Как сказал Грег Хьюджилл, typedef означает, что вам больше не нужно писать struct по всему месту. Это не только экономит нажатия клавиш, но также может сделать код более чистым, поскольку он обеспечивает более абстракцию smidgen.

Как нравится

 typedef struct { int x, y; } Point; Point point_new(int x, int y) { Point a; ax = x; ay = y; return a; } 

становится чище, когда вам не нужно видеть ключевое слово «struct» по всему месту, оно выглядит скорее так, как если бы на вашем языке был тип «Point». Я думаю, что после typedef это случай.

Также обратите внимание, что, хотя ваш пример (и мой) опустил именование самой struct , на самом деле его имя также полезно, когда вы хотите предоставить непрозрачный тип. Тогда у вас будет такой код в заголовке, например:

 typedef struct Point Point; Point * point_new(int x, int y); 

а затем предоставить объявление struct в файле реализации:

 struct Point { int x, y; }; Point * point_new(int x, int y) { Point *p; if((p = malloc(sizeof *p)) != NULL) { p->x = x; p->y = y; } return p; } 

В этом последнем случае вы не можете вернуть значение Point по значению, так как его объявление скрыто от пользователей заголовочного файла. Это метод, широко используемый в GTK + , например.

UPDATE Обратите внимание, что есть также высокоценные проекты C, где это использование typedef для скрытия struct считается плохой идеей, kernel ​​Linux, вероятно, является самым известным из таких проектов. См. Главу 5 документа CodingStyle для ядра Linux для сердитых слов Линуса. 🙂 Я хочу сказать, что вопрос «должен» в вопросе, возможно, не установлен в камне, в конце концов.

Удивительно, как многие люди ошибаются. ПОЖАЛУЙСТА, не создавайте структуры typedef на C, он бесполезно загрязняет глобальное пространство имен, которое обычно сильно загрязняется уже в больших программах на C.

Кроме того, структуры typedef’d без имени тега являются основной причиной ненужного наложения отношений упорядочения между заголовочными файлами.

Рассматривать:

 #ifndef FOO_H #define FOO_H 1 #define FOO_DEF (0xDEADBABE) struct bar; /* forward declaration, defined in bar.h*/ struct foo { struct bar *bar; }; #endif 

С таким определением, не использующим typedefs, блок compiland может включать foo.h для определения FOO_DEF . Если он не пытается разыменовать элемент «bar» структуры foo тогда нет необходимости включать файл «bar.h».

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

 struct foo *foo; printf("foo->bar = %p", foo->bar); 

Поскольку пространства имен являются отдельными, конфликт переменных в именах не совпадает с их именем тега структуры.

Если мне нужно сохранить код, я удалю ваши структуры typedef’d.

Из старой статьи Дэна Сакса ( http://www.ddj.com/cpp/184403396?pgno=3 ):


Правила языка C для именования структур немного эксцентричны, но они довольно безвредны. Однако, когда они расширены для classов на C ++, те же правила открывают небольшие трещины для обхода ошибок.

В C имя s появляется в

 struct s { ... }; 

является тегом. Имя тега не является именем типа. Учитывая вышеприведенное определение, объявления, такие как

 sx; /* error in C */ s *p; /* error in C */ 

являются ошибками в C. Вы должны написать их как

 struct sx; /* OK */ struct s *p; /* OK */ 

Имена союзов и перечислений также являются тегами, а не типами.

В C tags отличаются от всех других имен (для функций, типов, переменных и констант enums). Компиляторы C поддерживают tags в таблице символов, которые концептуально, если не физически отделены от таблицы, содержащей все другие имена. Таким образом, программа C может иметь как тег, так и другое имя с одинаковой орфографией в той же области. Например,

 struct ss; 

является допустимым объявлением, которое объявляет переменную s типа struct s. Это не может быть хорошей практикой, но компиляторы C должны ее принять. Я никогда не видел обоснования того, почему C был спроектирован таким образом. Я всегда думал, что это ошибка, но вот она.

Многие программисты (в том числе и ваши) предпочитают думать о именах структур как имена типов, поэтому они определяют псевдоним для тега с использованием typedef. Например, определение

 struct s { ... }; typedef struct s S; 

позволяет использовать S вместо struct s, как в

 S x; S *p; 

Программа не может использовать S как имя как типа, так и переменной (или функции или константы enums):

 SS; // error 

Это хорошо.

Имя тега в определении структуры, объединения или enums является необязательным. Многие программисты сбрасывают определение структуры в typedef и обходятся без тега вообще, как в:

 typedef struct { ... } S; 

Связанная статья также содержит обсуждение того, как поведение C ++, не требующее typedef может вызвать проблемы со скрытым именем. Чтобы предотвратить эти проблемы, рекомендуется также typedef classы и структуры на C ++, даже если на первый взгляд это кажется ненужным. В C ++, с typedef скрытие имени становится ошибкой, о которой компилятор говорит вам, а не о скрытом источнике потенциальных проблем.

Использование typedef позволяет избежать необходимости писать struct каждый раз, когда вы объявляете переменную этого типа:

 struct elem { int i; char k; }; elem user; // compile error! struct elem user; // this is correct 

Еще одна веская причина всегда перечислять enums и структуры typedef:

 enum EnumDef { FIRST_ITEM, SECOND_ITEM }; struct StructDef { enum EnuumDef MyEnum; unsigned int MyVar; } MyStruct; 

Обратите внимание на опечатку в EnumDef в структуре (Enu u mDef)? Это компилируется без ошибок (или предупреждения) и соответствует (в зависимости от буквальной интерпретации стандарта C). Проблема в том, что я только что создал новое (пустое) определение enums в моей структуре. Я не (как предполагалось), используя предыдущее определение EnumDef.

С typdef подобный тип опечаток привел бы к ошибкам компилятора для использования неизвестного типа:

 typedef { FIRST_ITEM, SECOND_ITEM } EnumDef; typedef struct { EnuumDef MyEnum; /* compiler error (unknown type) */ unsigned int MyVar; } StructDef; StrructDef MyStruct; /* compiler error (unknown type) */ 

Я защищал бы ВСЕГДА печатные структуры и enums.

Не только сохранить некоторую типизацию (каламбур не предназначен;)), а потому, что это безопаснее.

Стиль кодирования ядра Linux Глава 5 дает большие плюсы и минусы (в основном минусы) использования typedef .

Пожалуйста, не используйте такие вещи, как «vps_t».

Ошибочно использовать typedef для структур и указателей. Когда вы видите

 vps_t a; 

в источнике, что это значит?

Напротив, если он говорит

 struct virtual_container *a; 

вы действительно можете сказать, что такое «а».

Многие люди думают, что typedefs «помогают читать». Не так. Они полезны только для:

(a) полностью непрозрачные объекты (где typedef активно используется для скрытия того, что является объектом).

Пример: «pte_t» и т. Д. Непрозрачные объекты, к которым вы можете получить доступ только с помощью соответствующих функций доступа.

ЗАМЕТКА! Непрозрачность и «функции доступа» сами по себе не хороши. Причина, по которой мы имеем их для таких вещей, как pte_t и т. Д., Заключается в том, что на самом деле существует абсолютно нулевая доступная для портативного доступа информация.

(b) Очистить целочисленные типы, где абстракция помогает избежать путаницы, является ли она «int» или «long».

u8 / u16 / u32 – отлично тонкие typedefs, хотя они вписываются в категорию (d) лучше, чем здесь.

ЗАМЕТКА! Опять же, для этого должна быть причина . Если что-то «unsigned long», тогда нет причин для этого

 typedef unsigned long myflags_t; 

но если есть ясная причина, почему это при определенных обстоятельствах может быть «неподписанным int», а в других конфигурациях может быть «unsigned long», то непременно пойти вперед и использовать typedef.

(c) когда вы используете разреженный, чтобы буквально создать новый тип для проверки типов.

(d) Новые типы, идентичные стандартным типам C99, в определенных исключительных обстоятельствах.

Хотя для глаз и мозга потребуется лишь небольшое количество времени, чтобы привыкнуть к стандартным типам, например, «uint32_t», некоторые люди возражают против их использования в любом случае.

Поэтому допустимы типы U8 / u16 / u32 / u64 Linux и их эквиваленты, идентичные стандартным типам, хотя они не являются обязательными для вашего нового кода.

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

(e) Типы, безопасные для использования в пользовательском пространстве.

В некоторых структурах, видимых для пользовательского пространства, мы не можем требовать типы C99 и не можем использовать форму «u32» выше. Таким образом, мы используем __u32 и подобные типы во всех структурах, которые совместно используются в пользовательском пространстве.

Возможно, есть и другие случаи, но правило в основном должно быть НИКОГДА НЕ используйте typedef, если вы не можете четко соответствовать одному из этих правил.

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

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

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

Не так на языке программирования C. Использование typedef чаще всего нецелесообразно, но для запутывания использования структуры данных. Поскольку для объявления типа данных используется только {struct (6), enum (4), union (5)} число нажатий клавиш, почти не используется для сглаживания структуры. Является ли этот тип данных объединением или структурой? Использование простой декларации, отличной от typdefed, позволяет сразу узнать, какой тип.

Обратите внимание на то, как Linux написан с строгим уклоном от этой псевдонимы. Результат – минималистский и чистый стиль.

Оказывается, есть плюсы и минусы. Полезным источником информации является семантическая книга «Программирование экспертов C» ( глава 3 ). Вкратце, в C у вас несколько пространств имен: tags, типы, имена участников и идентификаторы . typedef вводит псевдоним для типа и находит его в пространстве имен тегов. А именно,

 typedef struct Tag{ ...members... }Type; 

определяет две вещи. Один тег в пространстве имен тегов и один тип в пространстве имен типов. Таким образом, вы можете сделать оба Type myType и struct Tag myTagType . Объявления типа struct Type myType или Tag myTagType являются незаконными. Кроме того, в заявлении вроде этого:

 typedef Type *Type_ptr; 

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

 Type_ptr var1, var2; struct Tag *myTagType1, myTagType2; 

то var1 , var2 и myTagType1 являются указателями на Type, но myTagType2 нет.

В вышеупомянутой книге упоминается, что typedefing structs не очень полезны, поскольку это только спасает программиста от написания слова struct. Однако у меня есть возражение, как и многие другие программисты на С. Хотя иногда это приводит к запутыванию некоторых имен (поэтому не рекомендуется в больших базовых кодах, таких как kernel), когда вы хотите реализовать polymorphism в C, это помогает подробно рассмотреть здесь подробности . Пример:

 typedef struct MyWriter_t{ MyPipe super; MyQueue relative; uint32_t flags; ... }MyWriter; 

ты можешь сделать:

 void my_writer_func(MyPipe *s) { MyWriter *self = (MyWriter *) s; uint32_t myFlags = self->flags; ... } 

Таким образом, вы можете получить доступ к внешнему элементу ( flags ) внутренней структурой ( MyPipe ) посредством кастинга. Для меня менее (struct MyWriter_ *) s; весь тип, чем делать (struct MyWriter_ *) s; каждый раз, когда вы хотите выполнять такие функции. В этих случаях краткая ссылка – это большое дело, особенно если вы сильно используете эту технику в своем коде.

Наконец, последний аспект с typedef типами – это невозможность их расширения, в отличие от макросов. Если, например, у вас есть:

 #define X char[10] or typedef char Y[10] 

вы можете объявить

 unsigned X x; but not unsigned Y y; 

Нам это не важно для структур, потому что это не относится к спецификаторам хранения ( volatile и const ).

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

GTK + в стороне, я не уверен, что тэг используется как обычно как typedef для типа структуры, поэтому в C ++, который распознается, и вы можете опустить ключевое слово struct и использовать тэг как имя типа:

struct MyStruct { int i; }; // The following is legal in C++: MyStruct obj; obj.i = 7;
struct MyStruct { int i; }; // The following is legal in C++: MyStruct obj; obj.i = 7; 

Начнем с основ и поработаем.

Ниже приведен пример определения структуры:

 struct point { int x, y; }; 

Здесь point имени необязательна.

Структура может быть объявлена ​​во время ее определения или после.

Объявление во время определения

 struct point { int x, y; } first_point, second_point; 

Объявление после определения

 struct point { int x, y; }; struct point first_point, second_point; 

Теперь внимательно обратите внимание на последний случай выше; вам нужно написать struct point для объявления структур этого типа, если вы решите создать этот тип в более поздней точке вашего кода.

Введите typedef . Если вы намереваетесь создать новую структуру (Structure – это пользовательский тип данных) в более позднее время в вашей программе, используя тот же план, использование typedef во время определения может быть хорошей идеей, так как вы можете сохранить некоторую ввод текста вперед.

 typedef struct point { int x, y; } Points; Points first_point, second_point; 

Слово предостережения при названии вашего пользовательского типа

Ничто не мешает вам использовать суффикс _t в конце вашего имени настраиваемого типа, но стандарт POSIX резервирует использование суффикса _t для обозначения имен стандартных типов библиотек.

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

 struct bar; struct foo; struct foo { struct bar *b; }; struct bar { struct foo *f; }; 

Конечно, вы всегда можете добавить:

 typedef struct foo foo_t; typedef struct bar bar_t; 

В чем именно смысл?

A> a typdef помогает в значении и документации программы, позволяя создавать более значимые синонимы для типов данных . Кроме того, они помогают параметризовать программу по проблемам переносимости (K & R, pg147, C prog lang).

B> структура определяет тип . Структуры позволяют удобно группировать коллекцию варов для удобства обработки (K & R, pg127, C prog lang.) Как единое целое

C> typedef’ing struct объясняется в A выше.

D> Для меня структуры представляют собой настраиваемые типы или контейнеры или коллекции или пространства имен или сложные типы, тогда как typdef – это всего лишь средство для создания большего количества псевдонимов.

Оказывается в C99 требуется typedef. Он устарел, но многие инструменты (ala HackRank) используют c99 в качестве своей чистой реализации C. И там typedef требуется.

Я не говорю, что они должны измениться (возможно, есть два варианта C), если это требование изменилось, те из нас, кто изучает интервью на сайте, будут SOL.

В языке программирования «C» ключевое слово «typedef» используется для объявления нового имени для какого-либо объекта (типа struct, array, function..enum). Например, я буду использовать «struct-s». В «C» мы часто объявляем «структуру» вне функции «main». Например:

 struct complex{ int real_part, img_part }COMPLEX; main(){ struct KOMPLEKS number; // number type is now a struct type number.real_part = 3; number.img_part = -1; printf("Number: %d.%di \n",number.real_part, number.img_part); } 

Каждый раз, когда я решаю использовать тип структуры, мне понадобится это ключевое слово ‘struct’ something ” name ‘.’ Typedef ‘просто переименует этот тип, и я могу использовать это новое имя в своей программе каждый раз, когда захочу. Таким образом, наш код будет:

 typedef struct complex{int real_part, img_part; }COMPLEX; //now COMPLEX is the new name for this structure and if I want to use it without // a keyword like in the first example 'struct complex number'. main(){ COMPLEX number; // number is now the same type as in the first example number.real_part = 1; number.img)part = 5; printf("%d %d \n", number.real_part, number.img_part); } 

Если у вас есть локальный объект (struct, array, value), который будет использоваться во всей вашей программе, вы можете просто присвоить ему имя, используя «typedef».

На языке C, struct / union / enum – это макрокоманда, обработанная препроцессором языка C (не ошибайтесь с препроцессором, который обрабатывает «#include» и другие)

так :

 struct a { int i; }; struct b { struct a; int i; int j; }; 

struct b используется как нечто вроде этого:

 struct b { struct a { int i; }; int i; int j; } 

и поэтому во время компиляции он развивается в стеке как-то вроде: b: int ai int i int j

что также важно иметь самонадеянные структуры, препроцессор C раунд в цикле деления, который не может завершиться.

typedef – это спецификатор типа, это означает, что только компилятор C обрабатывает его, и он может делать так, как он хочет оптимизировать реализацию кода ассемблера. Он также не расходует член типа par, по-тупо, как préprocessor, с конструкциями, но использует более сложный алгоритм построения ссылок, поэтому построение вроде:

 typedef struct a A; //anticipated declaration for member declaration typedef struct a //Implemented declaration { A* b; // member declaration }A; 

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

Это означает, что в C typedefs более близки к classу C ++, чем одинокие структуры.

  • Как typedef работает для указателей функций
  • Как вы читаете декларации C?
  • Повторяющиеся typedefs - недопустимы в C, но действительны в C ++?
  • Преобразование цели-c typedef в его эквивалент строки
  • uint8_t против символа без знака
  • Что означает typedef с круглыми скобками типа «typedef int (f) (void)»? Это прототип функции?
  • Почему мне нужно использовать typedef typename в g ++, но не VS?
  • Являются ли typedef и #define одинаковыми в c?
  • Статический polymorphism C ++ (CRTP) и использование typedefs из производных classов
  • Что такое «форвардная декларация» и разница между «typedef struct X» и «struct X»?
  • C ++ typedef интерпретация константных указателей
  • Давайте будем гением компьютера.