Чому ми повинні так часто вводити структуру в C?


406

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

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

elem user;

Чому це потрібно так часто? Якась конкретна причина чи застосовна область?


14
Більш ретельний і точний відповідь: stackoverflow.com/questions/612328 / ...
AbiusX

У нього є недоліки. Я думаю, що ви не можете створити список посилань з анонімною структурою, оскільки рядок struct * ptrвсередині структури призведе до помилки
Raghib Ahsan

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

Це запитання має дублікат typedef struct проти struktur, що також має зоряні відповіді.
Джонатан Леффлер

2
OTOH, kernel.org/doc/html/v4.10/process/coding-style.html говорить нам, що ми не повинні робити таких типів.
glglgl

Відповіді:


454

Як сказав Грег Х'югілл, 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;
}

В останньому випадку ви не можете повернути точку за значенням, оскільки її визначення приховано від користувачів файлу заголовка. Наприклад, це методика, широко застосовувана в 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 , Усі ідентифікатори, які починаються з підкреслення, або великими літерами, або іншим підкресленням завжди зарезервовані для будь-якого використання. І всі ідентифікатори, що починаються з підкреслення, завжди зарезервовані для використання в якості ідентифікаторів з логічним розмахом як у звичайному просторі, так і в місцях імен тегів.
e2-e4

10
Цікаво, що керівництво щодо кодування ядра Linux говорить про те, що ми повинні бути набагато консервативнішими щодо використання typedefs (розділ 5): kernel.org/doc/Documentation/CodingStyle
gsingh2011

206

Дивно, скільки людей помиляються на цьому. БУДЬ ЛАСКА не вводити структури в 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визначення. Якщо він не намагатиметься fooзнеструмити "bar"-член структури, тоді не потрібно буде включати файл "bar.h".

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

struct foo *foo;

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

Оскільки простори імен є окремими, конфлікту в іменуванні змінних, що збігаються з назвою їх структурного тегу, немає.

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


37
Що більш дивовижне - це те, що через 13 місяців після даної відповіді я першим виступив із цим рішенням! typedef'ing structs - одна з найбільших зловживань C, і не має місця в добре написаному коді. typedef є корисним для знешкодження типів вказівників зведених функцій і справді не виконує ніяких інших корисних цілей.
Вільям Перселл

28
Пітер ван дер дер Лінден також розглядає справу проти наборотих структур у своїй освітянській книзі "Експертне програмування на C - глибокі секрети C". Суть полягає в тому, що ви хочете знати, що щось є структурою чи об'єднанням, а не приховувати це.
Єнс

34
Стиль кодування ядра Linux явно забороняє вводити структури defdefing. Глава 5: Typedefs: " Помилково використовувати typedef для структур та покажчиків." kernel.org/doc/Documentation/CodingStyle
jasso

63
Яку саме користь дає типізація "структури" знову і знову? А якщо говорити про забруднення, то чому ви хочете мати структуру та функцію / змінну / typedef з тим самим іменем у глобальному просторі імен (якщо це не typedef для тієї самої функції)? Безпечний зразок - використання typedef struct X { ... } X. Таким чином, ви можете використовувати коротку форму Xдля адреси структури де завгодно, щоб визначення було доступне, але все одно можете переадресувати, оголосити та використовувати struct Xза бажанням.
Павло Мінаєв

7
Я особисто дуже рідко, якщо коли-небудь використовувати typedef, я б не сказав, що інші люди не повинні його використовувати, це просто не мій стиль. Мені подобається бачити структуру перед змінним типом, тож я відразу знаю її структуру Аргумент простіше вводити трохи кульгаво, а змінні, які є однією буквою, також простіше набрати, також з автоматичними доповненнями, наскільки важко вводити структуру сьогодні в будь-якому місці.
День Натана

138

Зі старої статті Дена Сакса ( http://www.ddj.com/cpp/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 підтримують теги в таблиці символів, яка концептуально, якщо фізично не відокремлена від таблиці, що містить усі інші імена. Таким чином, можливо, що програма 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 ++, яка не вимагає, typedefможе спричинити неповторні проблеми приховування імен. Щоб запобігти цим проблемам, також корисно для typedefваших занять і структур на C ++, навіть якщо на перший погляд це видається непотрібним. У C ++ typedefприховування імені стає помилкою, про яку компілятор розповідає, а не прихованим джерелом потенційних проблем.


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

2
ваша заява, СС; // Помилка .... НЕ ПРАВИЛЬНО Добре працює. Я маю на увазі ваше твердження про те, що "ми не можемо мати те саме ім'я для typedef тегів і var" є WRONG ... pls check
eRaisedToX

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?
Manoj Doubts

4
Маной, назва тегу ("struct foo") необхідна, коли потрібно визначити структуру, на яку посилається сама. наприклад, "наступний" вказівник у пов'язаному списку. Більш суттєвим є те, що компілятор реалізує стандарт, і саме це говорить стандарт.
Майкл Карман

41
Це не проблема в компіляторі C, це частина дизайну. Вони змінили це для C ++, що, на мою думку, полегшує справи, але це не означає, що поведінка C неправильна.
Гермс

5
на жаль, багато 'програмістів' визначають структуру, а потім набирають її якоюсь "неспорідненою" назвою (наприклад, структуру myStruct ...; typedef struct myStruct susan *; майже у всіх випадках типdef не призводить до нічого, крім захаращування коду, приховуючи фактичне визначення змінна / параметр, а також неправильно веде всіх, включаючи оригінальний автор коду.
user3629249

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

38

Ще одна вагома причина, щоб завжди вводити dedef перераховує та структурує результат цієї проблеми:

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

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

Помітьте друкарські помилки в EnumDef у структурі (Enu u Enu mDef)? Це компілюється без помилок (або попередження) і є (залежно від буквального тлумачення стандарту С) правильним. Проблема полягає в тому, що я щойно створив нове (порожнє) визначення перерахунку в межах своєї структури. Я не (за призначенням) використовую попереднє визначення 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;' не визначає enum. Я написав сотні величезних програм і мав нещастя виконувати технічне обслуговування програм, які написали інші. З важкого досвіду використання typedef в структурі призводить лише до проблем. Сподіваємось, програміст не настільки невдалий, що у них виникають проблеми з набором повного визначення, коли вони оголошують структурний екземпляр. C не є базовим, тому введення ще декількох символів не шкодить роботі програми.
користувач3629249

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

Зауважте, зазвичай це не рекомендується використовувати enumяк тип, оскільки багато компіляторів випробовують дивні попередження, коли вони використовують "неправильно". Наприклад, ініціалізація enumдо 0 може подати попередження про «цілу константу, що не перебуває у перерахуванні». Попереднє оголошення оголошення enumтакож не дозволено. Натомість слід використовувати int(або unsigned int).
yyny

5
Цей приклад не складається, і я б не очікував цього. Компіляція налагодження / test.o test.c: 10: 17: помилка: поле має неповний тип 'enum EnuumDef' enum EnuumDef MyEnum; ^ test.c: 10: 8: Примітка: попереднє оголошення 'enum EnuumDef' enum EnuumDef MyEnum; ^ Створено 1 помилку gnuc, з std = c99.
natersoz

30

Стиль кодування ядра Linux Розділ 5 дає великі плюси і мінуси (в основному мінуси) використання typedef.

Будь ласка, не використовуйте речі типу "vps_t".

Це є помилка використовувати ЬурейеЕ для структур і покажчиків. Коли ви бачите a

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;

але якщо є чітка причина того, чому він за певних обставин може бути "непідписаним int", а в інших конфігураціях може бути "непідписаним довгим", тоді, будь-ласка, йдіть вперед і використовуйте typedef.

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

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

Хоча очі і мозок займуть лише короткий час, щоб звикнути до стандартних типів, таких як 'uint32_t', деякі люди все одно заперечують проти їх використання.

Тому типи 'u8 / u16 / u32 / u64' для Linux та їх підписані еквіваленти, які є ідентичними стандартним типам, дозволені, хоча вони не є обов'язковими для нового власного коду.

Під час редагування існуючого коду, який вже використовує той чи інший набір типів, ви повинні відповідати існуючим варіантам цього коду.

(e) типи, безпечні для використання в просторі користувачів.

У певних структурах, видимих ​​для простору користувачів, ми не можемо вимагати типів C99 і не можемо використовувати форму 'u32' вище. Таким чином, ми використовуємо __u32 та подібні типи у всіх структурах, які спільно використовуються з простором користувачів.

Можливо, є й інші випадки, але в основному правило повинно бути таким, щоб НІКОЛИ не застосовувати typedef, якщо ви не зможете чітко відповідати одному з цих правил.

Взагалі, вказівник або структура, яка має елементи, до яких можна легко отримати доступ, ніколи не повинна бути typedef.


4
"Непрозорість та" функції аксесуарів "самі по собі не є добрими". Може хтось пояснить, чому? Я думаю, що приховування та капсулювання інформації було б дуже хорошою ідеєю.
Явар

5
@Yawar Я щойно прочитав цей документ і мав абсолютно таку ж думку. Звичайно, C не орієнтована на об'єкти, але абстракція все-таки річ.
Балдрік

12

Виявляється, є плюси і мінуси. Корисним джерелом інформації є семінарна книга «Експертне програмування 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немає.

У вищезгаданій книзі згадується, що введення структур не дуже корисно, оскільки це лише рятує програміста від написання слова структура. Однак у мене є заперечення, як і у багатьох інших програмістів на С. Хоча іноді звертається до придушення деяких імен (тому це не доцільно у великих кодових базах, таких як ядро), коли ви хочете реалізувати поліморфізм у 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-квітень / msg00196.html
підкреслюйте_d

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

12

Я не думаю, що форвардні декларації можливі навіть з typedef. Використання структури, перерахунку та об'єднання дозволяють пересилати декларації, коли залежності (знають про них) двосторонні.

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

Не так у мові програмування на С. Використання typedef найчастіше не має жодної мети, крім придушення використання структури даних. Оскільки для декларування типу даних використовується лише {struct (6), enum (4), union (5)} кількість натискань клавіш, то для псевдоніму структури майже немає користі. Це тип даних об'єднання чи структура? Використання прямої недекларованої декларації дає змогу одразу дізнатися, що це за тип.

Зауважте, як пишеться Linux із суворим уникненням цього придурливого дурницького типуdef. Результат - мінімалістичний та чистий стиль.


10
Чисте б не повторювалося structскрізь ... Typedef створює нові типи. Що ви використовуєте? Типи. Нам не байдуже, чи це структура, союз чи перелік, тому ми вводимо це.
GManNickG

13
Ні, ми б все одно , якщо це структура або об'єднання, по порівнянні з перерахуванням або який - то атомарного типу. Ви не можете примусити структуру до цілого чи до вказівника (або будь-якого іншого типу, з цього приводу). Це все, що вам іноді доводиться зберігати в якомусь контексті. Наявність ключових слів "struct" або "union" навколо покращує локальність міркувань. Ніхто не каже, що вам потрібно знати, що всередині структури.
Бернд Джендрісек

1
@BerndJendrissek: Структури та об'єднання відрізняються від інших типів, але чи повинен клієнтський код дбати про те, що з цих двох речей (структура чи об'єднання) є чимось подібним FILE?
supercat

4
@supercat FILE - це гарне використання typedef. Я вважаю, що typedef перебільшено , не те, що це неправильна мова. ІМХО, що використовує typedef для всього, - це запах кодової спекулятивної надвиробничості. Зауважте, що ви оголошуєте змінні як FILE * foo, ніколи не FILE foo. Для мене це має значення.
Бернд Джендрісек

2
@supercat: "Якщо змінні, що ідентифікують файли, мали тип FILE, а не FILE * ..." Але саме таку неоднозначність дає змогу typedefs! Ми просто звикли робити FILE *, тому ми не турбуємося про це, але кожен раз, коли ви додаєте typedef, ви вводите ще один пізнавальний наклад: чи цей API хоче fog_t arg або foo_t *? Явне перенесення «структури» вздовж покращує локальність міркувань, якщо ціною ще декількох символів на визначення функції.
Бернд Джендрісек

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

ім'я, яке ви (необов'язково) даєте структурі, називається ім'ям тега і, як було зазначено, само по собі не є типом. Щоб дійти до типу, потрібен префікс struk.

GTK + убік, я не впевнений, що в імені тегів використовується що-небудь на зразок типу typedef до типу структура, тому в C ++, що розпізнається, ви можете опустити ключове слово stru і використовувати тег як ім'я типу:


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

B> структура визначає тип . Structs дозволяє зручно групувати колекцію вар для зручності роботи (K&R, pg127, C prog lang.) Як єдине ціле

C> типзростання структури пояснюється в А вище.

D> Для мене структури - це власні типи або контейнери, колекції чи простори імен або складні типи, тоді як tyfdef - це лише спосіб створити більше прізвиськ.


0

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

Я не кажу, що вони повинні змінитися (можливо, мають два варіанти С), якщо вимога зміниться, ті, хто нас, хто навчається на інтерв'ю на сайті, будуть SOL.


2
"Виявляється, що в C99 typedefпотрібно". Що ви маєте на увазі?
Жульєн Лопес

Питання в тому C, чи ні C++. У Ctypedefs є "обов'язкові" (і, швидше за все, завжди будуть). "Обов'язково", як і в, ви не зможете оголосити змінну Point varName;і матимете тип синонімом struct Point;без а typedef struct Point Point;.
yyny

0

У мові програмування 'C' ключове слово 'typedef' використовується для оголошення нового імені для якогось об'єкта (тип структури, масив, функція ... кількість). Наприклад, я буду використовувати "struct-s". У "С" ми часто оголошуємо "структуру" поза "головної" функції. Наприклад:

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, і він може робити так, як він хоче для оптимізації реалізації коду асемблера. Він також не витрачає члена типу типу тупо, як préprocessor робити зі структурами, але використовує більш складний алгоритм побудови еталону, так що конструкція, як:

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 * далі; int річ; }
Бернд Джендрісек

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.