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

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

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

elem user;

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

Ответ 1

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

Такие вещи, как

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 +, например.

UPDATE Обратите внимание, что есть также высокоценные проекты C, где использование typedef для скрытия struct считается плохой идеей, ядро ​​Linux, вероятно, является наиболее известным проект. См. Главу 5 Документ ядра KodingStyle Linux для гневных слов Линуса.:) Я хочу сказать, что "вопрос" в вопросе, возможно, не установлен в камне, в конце концов.

Ответ 2

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

Ответ 3

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


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

В C имя s, появляющееся в

struct s
    {
    ...
    };

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

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

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

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

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

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

struct s s;

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

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

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

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

S x;
S *p;

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

S S;    // error

Это хорошо.

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

typedef struct
    {
    ...
    } S;

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

Ответ 4

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

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

Ответ 5

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

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

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

Обратите внимание на опечатку в EnumDef в структуре (Enu u mDef)? Это компилируется без ошибок (или предупреждения) и соответствует (в зависимости от буквальной интерпретации стандарта C). Проблема в том, что я только что создал новое (пустое) определение перечисления в моей структуре. Я не (как предполагалось), используя предыдущее определение 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) */

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

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

Ответ 6

Стиль кодирования ядра 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;

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

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

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

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

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

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

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

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

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

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

Ответ 7

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

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

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

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

Ответ 8

Оказывается, что есть пробки и минусы. Полезным источником информации является семантическая книга "Программирование экспертов 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;

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

В вышеупомянутой книге упоминается, что typedefing structs не очень полезно, поскольку это только спасает программиста от написания слова 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;

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

Ответ 9

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

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


    struct MyStruct
    {
      int i;
    };

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

Ответ 10

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;

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

Ответ 11

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

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' 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".

Ответ 12

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

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

Ответ 13

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

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

С > typedef'ing struct объясняется в выше.

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

Ответ 14

На языке 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;
}

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

что также важно, чтобы иметь selfreferent structs, препроцессор 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;

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

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