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

Старое решение школы для этого без цикла / курсора состоит в создании таблицы NUMBERS, которая имеет один столбец Integer со значениями, начинающимися с 1.

CREATE TABLE  `example`.`numbers` (
  `id` int(10) unsigned NOT NULL auto_increment,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

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

INSERT INTO NUMBERS (id) VALUES (NULL);

После того, как у вас есть таблица NUMBERS, вы можете использовать:

SELECT x.start_date + INTERVAL n.id-1 DAY
  FROM NUMBERS n
  JOIN (SELECT STR_TO_DATE('2010-01-20', '%Y-%m-%d') AS start_date 
          FROM DUAL) x
 WHERE x.start_date + INTERVAL n.id-1 DAY <= '2010-01-24'

Абсолютное низкотехнологичное решение be:

SELECT STR_TO_DATE('2010-01-20', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-21', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-22', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-23', '%Y-%m-%d')
 FROM DUAL
UNION ALL
SELECT STR_TO_DATE('2010-01-24', '%Y-%m-%d')
 FROM DUAL

Для чего вы его используете?


Чтобы генерировать списки дат или цифр, чтобы ЛЕВАЯ ВСТУПЛЕНИЕ на. Вы должны были бы это сделать, чтобы увидеть, где есть пробелы в данных, потому что вы LEFT JOINING в список секвенциальных данных - нулевые значения станут очевидными там, где существуют пробелы.

368
задан nbro 18 March 2016 в 01:28
поделиться

15 ответов

Как сказал Грег Хьюгилл, 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 + .

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

431
ответ дан ratchet freak 18 March 2016 в 01:28
поделиться

Вообще говоря, в языке 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 тупо, как это делает препроцессор со структурами, но использует более сложный алгоритм построения ссылок, поэтому конструкция вроде:

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

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

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

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

-2
ответ дан doccpu 18 March 2016 в 01:28
поделиться

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;

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

1
ответ дан natersoz 18 March 2016 в 01:28
поделиться

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

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

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 для обозначения стандартного типа библиотеки. имена.

4
ответ дан Yoon5oo 18 March 2016 в 01:28
поделиться

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

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct
59
ответ дан Greg Hewgill 18 March 2016 в 01:28
поделиться

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

198
ответ дан Jens 18 March 2016 в 01:28
поделиться

В языке программирования «C» ключевое слово «typedef» используется для объявления нового имени некоторого объекта (struct, array, function..enum type). Например, я буду использовать 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.

0
ответ дан Yoon5oo 18 March 2016 в 01:28
поделиться

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

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

0
ответ дан Matthew Corey Brown 18 March 2016 в 01:28
поделиться

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

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

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

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

1
ответ дан JamesAD-0 18 March 2016 в 01:28
поделиться

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

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


    struct MyStruct
    {
      int i;
    };

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

3
ответ дан philsquared 18 March 2016 в 01:28
поделиться
1124 Оказывается, есть плюсы и минусы. Полезным источником информации является основополагающая книга «Программирование на 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 являются указателями на Type, но 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).

10
ответ дан Yoon5oo 18 March 2016 в 01:28
поделиться

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

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) */

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

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

38
ответ дан M.M 18 March 2016 в 01:28
поделиться

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


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

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;

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

137
ответ дан 2 revs, 2 users 99% 18 March 2016 в 01:28
поделиться

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

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

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

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

10
ответ дан Yoon5oo 18 March 2016 в 01:28
поделиться

Стиль кодирования ядра 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) лучше, чем здесь.

ВНИМАНИЕ! Опять же - для этого должна быть причина . Если что-то «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.

29
ответ дан svick 18 March 2016 в 01:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: