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


407

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

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

elem user;

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


14
Более подробный и точный ответ: stackoverflow.com/questions/612328/…
AbiusX

У этого есть недостатки, я думаю, вы не можете создать список ссылок с анонимной структурой, потому что строка struct * ptrвнутри структуры вызовет ошибку
Raghib Ahsan

9
«Более тщательный и точный ответ» - это разница между struct и typedef struct в C ++ , и в этой области существуют значительные различия между C и C ++, которые делают этот ответ не совсем подходящим для вопроса о C.
Джонатан Леффлер

Этот вопрос имеет дубликат определения типа typedef struct vs struct, который также имеет звездные ответы.
Джонатан Леффлер

2
OTOH, kernel.org/doc/html/v4.10/process/coding-style.html говорит нам, что мы не должны делать такие определения типов.
glglgl

Ответы:


455

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

Вещи как

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = 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 + .

ОБНОВЛЕНИЕ Обратите внимание, что существуют также высоко оцененные C-проекты, в которых использование typedefскрытия structсчитается плохой идеей, ядро ​​Linux, вероятно, является наиболее известным таким проектом. См. Главу 5 документа Linux Kernel CodingStyle для злых слов Линуса. :) Моя точка зрения заключается в том, что «следует» в вопросе, возможно, не в камне, в конце концов.


58
Не следует использовать идентификаторы с подчеркиванием, за которым следует заглавная буква, они зарезервированы (см. Раздел 7.1.3, пункт 1). Хотя это вряд ли является большой проблемой, это технически неопределенное поведение при их использовании (7.1.3, параграф 2).
Dreamlax

16
@dreamlax: В случае, если другим было непонятно, это только начало идентификатора с подчеркивания и заглавных букв, которые вы не должны делать; Вы можете использовать это в середине идентификатора.
brianmearns

12
Интересно, что приведенный здесь пример (в котором typedef предотвращает использование «struct» «повсеместно») на самом деле длиннее, чем тот же код без typedef, поскольку он сохраняет ровно одно использование слова «struct». Полученный образ абстракции редко сравнивается благоприятно с дополнительной обфускацией.
Уильям Перселл

7
@Rerito FYI, страница 166 из проекта C99 , Все идентифицируемых ERS , которые начинаются с символа подчеркивания и либо прописной буквы или другого символа подчеркивания всегда зарезервированы для любого использования. И все идентификаторы, которые начинаются с подчеркивания, всегда зарезервированы для использования в качестве идентификаторов с областью видимости как в обычном пространстве, так и в пространстве имен тега.
e2-e4

10
Интересно, что в руководствах по написанию кода в ядре Linux говорится, что мы должны быть более консервативны в использовании typedefs (раздел 5): kernel.org/doc/Documentation/CodingStyle
gsingh2011

206

Удивительно, как много людей ошибаются. ПОЖАЛУЙСТА, не печатайте структуры типов в 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);

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

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


37
Что еще более удивительно, то, что через 13 месяцев после того, как этот ответ дан, я первый, кто проголосовал за него! Структуры typedef'ing - одно из самых больших злоупотреблений C, и они не имеют места в хорошо написанном коде. typedef полезен для де-запутывания запутанных типов указателей на функции и действительно не служит никакой другой полезной цели.
Уильям Перселл

28
Питер ван дер Линден (Peter van der Linden) также приводит аргументы против структур определения типов в своей просвещающей книге «Программирование на языке C - секреты Deep C». Суть в том, что вы ХОТИТЕ знать, что что-то является структурой или объединением, а не скрывать это.
Дженс

34
Стиль кодирования ядра Linux явно запрещает структуры определения типов. Глава 5: Typedefs: « Ошибочно использовать typedef для структур и указателей». kernel.org/doc/Documentation/CodingStyle
JASSO

63
Какие именно преимущества дает ввод «struct» снова и снова? И если говорить о загрязнении, зачем вам иметь структуру и функцию / переменную / typedef с одинаковым именем в глобальном пространстве имен (если это не typedef для той же функции)? Безопасный образец должен использовать typedef struct X { ... } X. Таким образом, вы можете использовать краткую форму Xдля обращения к структуре везде, где доступно определение, но все же можете объявить и использовать ее struct Xпри необходимости.
Павел Минаев

7
Лично я очень редко, если когда-либо использую typedef, я бы не сказал, что другие люди не должны использовать его, это просто не мой стиль. Мне нравится видеть структуру перед типом переменной, поэтому я сразу знаю, что это структура. Аргумент проще набрать, потому что переменную, состоящую из одной буквы, также легче набирать, а также с автозаполнением, как трудно печатать struct в настоящее время в любом месте.
Натан День

138

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


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

В С, имя появляется в

struct s
    {
    ...
    };

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

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

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

struct s x;     /* OK */
struct s *p;    /* OK */

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

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

struct s s;

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

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

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

позволяет использовать S вместо структуры S, как в

S x;
S *p;

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

S S;    // error

Это хорошо.

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

typedef struct
    {
    ...
    } S;

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


2
Один из примеров того, где имя тега совпадает с именем без тега, приведен в программе (POSIX или Unix) с int stat(const char *restrict path, struct stat *restrict buf)функцией. Там у вас есть функция statв обычном пространстве имен и struct statв пространстве имен тегов.
Джонатан Леффлер

2
ваше заявление, СС; // ошибка .... НЕПРАВИЛЬНО Работает хорошо. Я имею в виду, что ваше утверждение о том, что «мы не можем иметь одно и то же имя для тэгов typedef и var» неверно ...
пожалуйста,

63

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

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

2
хорошо, у нас нет этой проблемы в C ++. Так почему никто не удаляет этот глюк из компилятора C и делает его таким же, как в C ++. Хорошо, C ++ имеет несколько различных областей применения и поэтому имеет расширенные возможности. Но мы не можем наследовать некоторые из них в C без изменения оригинальный C?
Маной сомневается

4
Manoj, имя тега ("struct foo") необходимо, когда вам нужно определить структуру, которая ссылается на себя. например, «следующий» указатель в связанном списке. Более того, компилятор реализует стандарт, и это то, что говорит стандарт.
Майкл Карман

41
Это не глюк в компиляторе C, это часть дизайна. Они изменили это для C ++, что, я думаю, облегчает ситуацию, но это не означает, что поведение C неверно.
Herms

5
к сожалению, многие «программисты» определяют структуру, а затем определяют ее, используя не связанное с ней имя (например, struct myStruct ...; typedef struct myStruct susan *; почти во всех случаях typedef не приводит ни к чему, кроме беспорядка в коде, скрывая фактическое определение переменная / параметр, и вводит в
заблуждение

1
@ user3629249 Я согласен с вами, что упомянутый стиль программирования ужасен, но это не повод клеветать на typedefструктуры в целом. Вы могли бы также сделать typedef struct foo foo;. Конечно, structключевое слово больше не требуется, что может быть полезным указанием на то, что псевдоним типа, на который мы смотрим, является псевдонимом для структуры, но в целом он неплох. Рассмотрим также случай , когда идентификатор результирующего типа псевдоним typedefуказывает на то, что он является псевдонимом для структуры, Fe: typedef struct foo foo_struct;.
RobertS поддерживает Монику Челлио

38

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

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

struct StructDef
{
  enum EnuumDef MyEnum;
  unsigned int MyVar;
} MyStruct;

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

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

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

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

Я бы поддержал ВСЕГДА набирать структуры и перечисления.

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


1
Хуже того, ваша опечатка может совпадать с другим тегом. В случае структуры это может привести к правильной компиляции всей программы и неопределенному поведению во время выполнения.
ММ

3
это определение: 'typedef {FIRST_ITEM, SECOND_ITEM} EnumDef;' не определяет перечисление. Я написал сотни огромных программ, и мне не повезло в обслуживании программ, написанных другими. Исходя из сложного опыта, использование typedef в структуре приводит только к проблемам. Надеюсь, у программиста нет таких недостатков, что у них возникают проблемы при наборе полного определения при объявлении экземпляра структуры. C не является базовым, поэтому ввод еще нескольких символов не наносит ущерба работе программы.
user3629249

2
Для тех, кто испытывает некоторое отвращение к набору текста, превышающему абсолютное минимальное количество символов, могу предложить присоединиться к группе, которая пытается писать приложения с минимальным количеством нажатий клавиш. Только не используйте их новый «навык» в рабочей среде, особенно в рабочей среде, которая строго выполняет рецензирование
user3629249

Обратите внимание, что обычно не рекомендуется использовать его enumкак тип, так как многие компиляторы выдают странные предупреждения, когда используют их «неправильно». Например, инициализация enumв 0 может дать предупреждение «целочисленная константа не в перечислении». Объявление вперед enumтакже не допускается. Нужно использовать int(или unsigned int) вместо этого.
yyny

5
Этот пример не компилируется, и я бы этого не ожидал. Компиляция Debug / test.o test.c: 10: 17: ошибка: поле имеет неполный тип 'enum EnuumDef' enum EnuumDef MyEnum; ^ test.c: 10: 8: note: предварительное объявление enum EnuumDef enum EnuumDef MyEnum; ^ 1 ошибка сгенерирована. gnuc, с std = c99.
natersoz

30

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

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

Это ошибка использовать ЬурейеЕ для структур и указателей. Когда вы видите

vps_t a;

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

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

struct virtual_container *a;

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

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

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

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

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

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

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

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

typedef unsigned long myflags_t;

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

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

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

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

Следовательно, специфичные для Linux типы «u8 / u16 / u32 / u64» и их подписанные эквиваленты, которые идентичны стандартным типам, разрешены, хотя они не являются обязательными в новом собственном коде.

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

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

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

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

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


4
«Непрозрачность и« функции доступа »сами по себе не годятся». Может кто-нибудь объяснить, почему? Я думаю, что сокрытие информации и инкапсуляция были бы очень хорошей идеей.
Явар

5
@ Явар Я только что прочитал этот документ и думал точно так же. Конечно, C не объектно-ориентирован, но абстракция все еще вещь.
Baldrickk

12

Оказывается, есть плюсы и минусы. Полезным источником информации является оригинальная книга «Программирование на Expert C» ( глава 3 ). Вкратце, в C у вас есть несколько пространств имен: теги, типы, имена членов и идентификаторы . 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указатели на тип , но myTagType2нет.

В вышеупомянутой книге упоминается, что структуры определения типов не очень полезны, поскольку они только спасают программиста от написания слова struct. Однако у меня есть возражение, как и у многих других программистов на Си. Хотя иногда оказывается необходимым запутывать некоторые имена (именно поэтому это не рекомендуется в больших базах кода, таких как ядро), когда вы хотите реализовать полиморфизм в 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;каждый раз, когда вы хотите выполнить такую ​​функциональность. В этих случаях краткие ссылки имеют большое значение, особенно если вы интенсивно используете технику в своем коде.

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

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

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

unsigned X x; but not
unsigned Y y;

Мы на самом деле не заботимся об этом для структур, потому что это не относится к спецификаторам хранения ( volatileи const).


1
MyPipe *s; MyWriter *self = (MyWriter *) s;и вы только что нарушили строгое наложение.
Джонатон Рейнхарт

@JonathonReinhart Было бы наглядно упомянуть, как этого можно избежать, например, как обходится безумно радующий GTK +: bugzilla.gnome.org/show_bug.cgi?id=140722 / mail.gnome.org/archives/gtk -devel-list / 2004-April / msg00196.html
underscore_d

"typedef вводит псевдоним для типа и находит его в пространстве имен тега. А именно" typedef struct Tag{ ...members... }Type; "определяет две вещи" не совсем имеет смысла. если typedef определяет теги, то здесь Type также должен быть тегом. Правда это определение определяет 2 метки и 1 -го типа (или 2 типа и 1 тег не уверен.): struct Tag, TagИ Type. struct Tagэто определенно тип. Tagэто тег но путаница заключается в том, является ли Typeтег или тип
Qwertyzw

12

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

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

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

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


10
Чистота не повторится structповсюду ... Typedef делает новые типы. Что ты используешь? Типы. Нам не важно , структура это, объединение или перечисление, поэтому мы его определяем.
GManNickG

13
Нет, нам все равно, является ли это структурой или объединением, в отличие от перечисления или некоторого атомарного типа. Вы не можете привести структуру к целому числу или к указателю (или к любому другому типу, если на то пошло), и это все, что вам иногда требуется для хранения некоторого контекста. Наличие ключевых слов «struct» или «union» вокруг улучшает локальность рассуждений. Никто не говорит, что вам нужно знать, что внутри структуры.
Бернд Джендриссек

1
@BerndJendrissek: Структуры и объединения отличаются от других типов, но должен ли клиентский код заботиться о том, какая из этих двух вещей (структура или объединение) чем-то похожа на a FILE?
суперкат

4
@supercat FILE - хорошее использование typedef. Я думаю , что ЬурейеЕ является злоупотребляют , не то, что это ошибочная особенность языка. ИМХО использование typedef для всего - это запах кода "умозрительной сверхгенеральности". Обратите внимание, что вы объявляете переменные как FILE * foo, а не как FILE foo. Для меня это важно.
Бернд Джендриссек

2
@supercat: «Если переменные, идентифицирующие файл, имели тип FILE, а не FILE * ...» Но это именно та неоднозначность, которую допускают typedefs! Мы просто привыкли брать FILE *, поэтому мы не беспокоимся об этом, но каждый раз, когда вы добавляете typedef, вы вводите еще одну часть когнитивных издержек: этот API требует foo_t args или foo_t *? Явное ношение «struct» вдоль улучшает локальность рассуждений, если за счет нескольких дополнительных символов на определение функции.
Бернд Джендриссек

4

Давайте начнем с основ и продолжим наш путь.

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

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

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

Points first_point, second_point;

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

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


3

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

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


    struct MyStruct
    {
      int i;
    };

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


1

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;

В чем именно смысл этого?


1

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

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

C> typedef'ing структура объяснена в A выше.

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


0

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

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


2
«Оказывается, в C99 typedefтребуется.» Что вы имеете в виду?
Жюльен Лопес

Вопрос в том C, нет C++. В Ctypedefs 'обязательны' (и, скорее всего, всегда будут). «Требуется», как в случае, вы не сможете объявить переменную Point varName;и иметь тип, который будет синонимичен struct Point;без typedef struct Point Point;.
yyny

0

В языке программирования «C» ключевое слово «typedef» используется для объявления нового имени для некоторого объекта (структура, массив, тип функции… типа). Например, я буду использовать struct-s. В «C» мы часто объявляем «struct» вне функции «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.%d i \n",number.real_part, number.img_part);

}

Каждый раз, когда я решу использовать тип структуры, мне понадобится ключевое слово struct что-то имя, 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);

}

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


-2

Вообще говоря, в языке C struct / union / enum - это макроинструкция, обрабатываемая препроцессором языка C (не путайте с препроцессором, который обрабатывает "#include" и другие)

так :

struct a
{
   int i;
};

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

структура b расходуется примерно так:

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

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

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

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

typedef struct a A; //anticipated declaration for member declaration

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

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

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


3
Нет ничего сложного в том, чтобы иметь ссылочные структуры. struct foo {struct foo * next; инт вещь; }
Бернд Джендриссек,

4
...что? Сказать препроцессор, чтобы описать разрешение structsи typedefs, достаточно плохо, но остальная часть вашего сочинения настолько сбивает с толку, что мне трудно получить какое-либо сообщение от него. Однако я могу сказать одну вещь: ваша идея о том, что non- typedefd structне может быть объявлен заблаговременно или использован как непрозрачный (указатель) член, полностью ложна. В вашем первом примере struct bможет содержать три struct a *, не typedefтребуется. Утверждения о том, что structs являются просто глупыми частями макроразложения, и которые typedefдают им революционно новые полномочия, до боли неверны
underscore_d
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.