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

StackOverflow https://stackoverflow.com/questions/252780

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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;
  a.x = x;
  a.y = y;
  return a;
}

становится чище, когда вам не нужно видеть " struct " Ключевое слово повсюду выглядит так, как будто действительно существует тип с именем " Точка " на вашем языке. Который, после 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 . за злые слова Линуса. :) Я хочу сказать, что " должен " в конце концов, вопрос, возможно, не в камне.

Другие советы

Удивительно, как много людей понимают это неправильно.ПОЖАЛУЙСТА, не используйте структуры 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, модуль компиляции может включать foo .h, чтобы получить доступ к FOO_DEF определение.Если он не пытается разыменовать элемент 'bar' в foo struct, тогда не будет необходимости включать файл "bar.h".

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

struct foo *foo;

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

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

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

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

<Ч>
  

Правила языка C для именования   структуры немного эксцентричны, но   они довольно безобидны. Однако когда   распространяется на классы в 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   должен принять это. Я никогда не видел   обоснование того, почему С был разработан этот   путь. Я всегда думал, что это было   ошибка, но это так.

     

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

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

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

S x;
S *p;
     

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

S S;    // error
     

Это хорошо.

     

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

typedef struct
    {
    ...
    } S;
<Ч>

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

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

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

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

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

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

Обратите внимание на опечатку в EnumDef в структуре (Enu u 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) */

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

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

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

ОБРАТИТЕ ВНИМАНИЕ!Опять же, должен быть Причина для этого.Если что-то "длинное без знака", то нет причин делать

typedef unsigned long myflags_t;

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

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

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

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

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

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

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

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

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

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

Оказывается, есть свои плюсы и минусы.Полезным источником информации является основополагающая книга "Экспертное программирование на языке Си". (Глава 3).Короче говоря, в C у вас есть несколько пространств имен: теги, типы, имена участников и идентификаторы. typedef вводит псевдоним для типа и размещает его в пространстве имен тегов.А именно,

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

определяет две вещи.Один тег в пространстве имен tag и один тип в пространстве имен 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редактирование типов заключается в невозможности их расширения, в отличие от макросов.Если, например, у вас есть:

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

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

unsigned X x; but not
unsigned Y y;

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

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

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

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

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

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

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

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 standard оставляет за собой право использовать суффикс _t для обозначения имен стандартных библиотечных типов.

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

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


    struct MyStruct
    {
      int i;
    };

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

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

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

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

C> определение типа структуры описано выше.

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

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

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

В языке программирования '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.

Вообще говоря, в языке 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

поэтому также трудно иметь самоссылающиеся структуры, цикл препроцессора C в цикле d & # 233; claration, который не может завершиться.

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top