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

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

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

elem user;

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

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

AbiusX 17.03.2011 05:14

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

Raghib Ahsan 01.02.2016 20:40

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

Jonathan Leffler 06.06.2016 01:21

У этого вопроса есть дубликат typedef struct vs struct определения, на который также есть звездные ответы.

Jonathan Leffler 06.06.2016 01:25

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

glglgl 27.03.2018 18:52
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
441
5
554 117
15
Перейти к ответу Данный вопрос помечен как решенный

Ответы 15

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

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

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

Manoj Doubts 31.10.2008 15:03

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

Michael Carman 31.10.2008 16:05

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

Herms 31.10.2008 22:36

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

user3629249 28.05.2015 23:50

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

RobertS supports Monica Cellio 10.02.2020 22:38
Ответ принят как подходящий

Как сказал Грег Хьюгилл, 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. :) Я хочу сказать, что «должен» в вопросе, в конце концов, не высечен на камне.

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

dreamlax 06.01.2011 13:40

Как насчет плюсов и минусов этого синтаксиса: typedef struct Point {int x, y; } Укажите эту ссылку: en.wikipedia.org/wiki/Struct_(C_programming_language)#typede‌ f обсуждает проблему, но не дает однозначного ответа на вопрос, в пользу она или нет.

dlchambers 04.10.2011 16:10

Ответ на мой вопрос: stackoverflow.com/questions/1110944/…

dlchambers 04.10.2011 16:14

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

brianmearns 16.01.2012 21:08

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

William Pursell 25.01.2012 18:15

@WilliamPursell Как вы считаете, чтобы получить один? Я вижу троих; один в заголовке и два в файле реализации. Может я просто запуталась.

unwind 18.12.2012 19:54

@unwind Прошло много месяцев, поэтому я не могу точно сказать, о чем думал, и согласен с вашим подсчетом. Возможно, я думал о следующем очень плохом стиле: заголовочный файл только с struct point * point_new(int x, int y); (объединение прямого объявления структуры с объявлением функции) и файл реализации struct point { int x,y; } * point_new( ... (объединение определения структуры с объявлением функции).

William Pursell 23.12.2012 21:01

@dreamlax Напомню, что многие коды Gstreamer определяют структуры с псевдонимом по шаблону _ [A-Z]. * ... И это работает очень хорошо. Не могли бы вы объяснить еще немного (ссылка в комментарии меня удовлетворила бы). Кстати, извините за "некромантинг"

Rerito 31.01.2013 01:42

@Rerito: Обычно в этом нет ничего страшного. Такие идентификаторы, как _[A-Z_].*, зарезервированы для использования в будущем. Например, C99 представил _Bool, а C11 представил _Atomic и _Thread_local (среди многих других). Все они были названы так, потому что они вышли из «зарезервированного» пула, и любой, кто уже использовал эти ключевые слова / идентификаторы в своем собственном коде, нарушил правило. как правило не представляет риска для использования _StructName и т. д. Технически согласно стандарту, использование такого имени является неопределенным поведением, но я еще не сталкивался с ситуацией, когда это имело значение.

dreamlax 31.01.2013 02:10

@Rerito fyi, стр. 166 из C99 черновик, Все идентификаторы, начинающиеся с символа подчеркивания и заглавной буквы или другого символа подчеркивания, всегда зарезервированы для любого использования. и Все идентификаторы, начинающиеся с подчеркивания, всегда зарезервированы для использования в качестве идентификаторов с областью видимости файлов как в обычном пространстве имен, так и в пространстве имен тегов.

e2-e4 15.04.2013 08:41

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

gsingh2011 29.05.2013 04:13

@dreamlax "Вы не должны использовать идентификаторы с подчеркиванием, за которым следует заглавная буква". Речь идет о подчеркивании имен с, за которым следует верхний регистр, или подчеркивании начиная с, за которым следует верхний регистр? ring0 предполагает, что это второе, но после прочтения вашего комментария я подумал, что это первый.

cubuspl42 05.07.2014 20:28

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

alternative 20.03.2015 00:37

@alternative: в некоторых реализациях лучше использовать структуру типа FILE; в других было бы лучше как союз. Если в конкретной реализации было бы лучше использовать объединение, то когда клиентский код везде будет указывать struct FILE *, реализация будет заключать объединение в бесполезную однокомпонентную структуру.

supercat 27.03.2015 21:53

@alternative: Почему только "стандартная" библиотека? Я думаю, что многие виды библиотек могли бы лучше всего работать в одной реализации, имея union в качестве внешнего члена, а в других - имея struct. Лично я думаю, что было бы лучше, если бы вместо того, чтобы типы профсоюзов, C представили более детальный контроль над структурой макета (например, struct quad { unsigned long l; unsigned char b[4] @ l;}), в этом случае, то этот вопрос будет спорным [на самом деле, C все еще выиграет от этого], но это очевидно не то, что произошло.

supercat 03.04.2015 18:58

@supercat, но важно, является ли непрозрачный объект структурой или объединением, вы должны сразу сказать. Если он непрозрачен, это не такая большая проблема, и все в стандартной библиотеке непрозрачно

alternative 09.04.2015 23:03

@alternative: если тип имеет более одного непрозрачного поля, очевидно, важна ли это структура или объединение. Я не вижу причин, по которым стандарт C должен быть уникальным, имея непрозрачные или почти непрозрачные типы, которые не раскрывают более одного поля.

supercat 09.04.2015 23:21

В вашем первом примере первая функция объявляет локальную переменную и возвращает эту переменную (точка a). Это вызовет предупреждение и является ошибкой. stackoverflow.com/questions/4824342/…

Milhous 13.10.2015 04:07

@Milhous Вовсе нет, я возвращаю значение, а не указатель.

unwind 14.10.2015 12:21

@unwind Ах, вы правы. но использование значений в целом - очень плохая практика. Внутри метода он скопирует данные все. Например, это нормально, так как есть 8 байтов. Но представьте, если бы это было несколько мегабайт.

Milhous 21.10.2015 04:53

@Milhous На мой взгляд, это просто ошибочные рассуждения. А Point - это не несколько мегабайт. Возвращаемые значения превосходны с точки зрения семантики и удобочитаемости, а быть супер-ультра-гиперзащитным и думать: «О нет, некоторые программы манипулируют данными, которые слишком велики, чтобы их можно было вернуть, давайте возвращаем значения никогда» для меня не имеет смысла. Используйте правильный инструмент и т. д.

unwind 22.10.2015 11:55

@unwind +1 Функции, возвращающие значение, также имеют то преимущество, что они могут быть помечены как __attribute __ ((const)) или чистые, и то и другое плохо работает с функциями, которые возвращаются через параметр указателя. К сожалению, простые компиляторы C плохо справляются с RVO (= вообще). Я однажды поигрался с этим, и g ++ имел на пару процентных пунктов более быстрый возврат по значению из-за копирования без RVO.

PSkocik 31.10.2016 22:51

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

AlphaGoku 17.07.2018 14:26

Я не понимаю вашу часть В последнем случае вы не можете вернуть Point по значению, поскольку его объявление скрыто от пользователей файла заголовка., почему она связана с typedef?

Kindred 23.11.2018 15:44

Не могли бы вы рассказать мне, как здесь работает if ((p = malloc(sizeof *p)) != NULL), хотя это и не связано с вопросом? Заранее спасибо.

LordOfThunder 23.06.2019 12:23

@AkshayImmanuelD Нет, поскольку структура обрабатывается как значение, она не отличается от возврата целого числа (сравните const int x = 17; return x;). Все значение копируется по значению, указатели не используются, поэтому место хранения оригинала не имеет значения.

unwind 24.06.2019 11:34

@taritgoswami Что сбивает с толку? Выражение sizeof *p - это способ во время компиляции вычислить количество байтов, необходимых для хранения того, на что указывает p, и устраняет необходимость вручную вводить имя типа в дополнительное время.

unwind 24.06.2019 11:34

Спасибо, а когда экспрессия (p = malloc(sizeof *p)) может быть NULL?

LordOfThunder 24.06.2019 22:28

@taritgoswami Функция malloc() возвращает NULL, если ей не удается выделить запрошенный объем памяти. Это базовые знания C.

unwind 26.06.2019 11:01

Повторный комментарий Уильяма Перселла повсюду означает, что структура используется в любое время

Paul 30.11.2019 07:41

Противоположная точка зрения: использование typedef struct Xyz XYZ и последующее определение переменной как имеющей тип XYZ вместо struct Xyz повышает фактор путаницы, потому что XYZ выглядит как элементарный тип, когда это является на самом деле struct.

Bob Jarvis - Reinstate Monica 06.06.2020 18:56

@ BobJarvis-ReinstateMonica: Еще одна причина в пользу ключевого слова struct: можно создавать и использовать указатели на неполные типы структур, идентифицированные с помощью ключевого слова struct и тега, без необходимости указания типа где-либо в модуле компиляции. Если кому-то может понадобиться использовать struct и имя тега, я думаю, что лучше последовательно указывать тип таким образом, чем иногда указывать его таким образом, а иногда использовать typedef.

supercat 26.06.2020 00:21

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

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


    struct MyStruct
    {
      int i;
    };

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

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


The C language rules for naming structs are a little eccentric, but they're pretty harmless. However, when extended to classes in C++, those same rules open little cracks for bugs to crawl through.

In C, the name s appearing in

struct s
    {
    ...
    };

is a tag. A tag name is not a type name. Given the definition above, declarations such as

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

are errors in C. You must write them as

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

The names of unions and enumerations are also tags rather than types.

In C, tags are distinct from all other names (for functions, types, variables, and enumeration constants). C compilers maintain tags in a symbol table that's conceptually if not physically separate from the table that holds all other names. Thus, it is possible for a C program to have both a tag and an another name with the same spelling in the same scope. For example,

struct s s;

is a valid declaration which declares variable s of type struct s. It may not be good practice, but C compilers must accept it. I have never seen a rationale for why C was designed this way. I have always thought it was a mistake, but there it is.

Many programmers (including yours truly) prefer to think of struct names as type names, so they define an alias for the tag using a typedef. For example, defining

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

lets you use S in place of struct s, as in

S x;
S *p;

A program cannot use S as the name of both a type and a variable (or function or enumeration constant):

S S;    // error

This is good.

The tag name in a struct, union, or enum definition is optional. Many programmers fold the struct definition into the typedef and dispense with the tag altogether, as in:

typedef struct
    {
    ...
    } S;

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

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

Jonathan Leffler 06.06.2016 01:02

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

eRaisedToX 03.08.2016 10:24

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

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

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

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

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

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

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

M.M 15.02.2015 01:15

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

user3629249 28.05.2015 23:57

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

user3629249 28.05.2015 23:59

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

yyny 01.12.2016 00:31

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

natersoz 21.01.2017 22:49

В clang ang gcc с c99 этот пример не компилируется. Но вроде Visual Studio ни на что не жалуется. rextester.com/WDQ5821

mja 19.06.2018 05:30

@ M.M «Хуже того, ваша опечатка может совпадать с другим тегом. В случае структуры это может привести к правильной компиляции всей программы и неопределенному поведению во время выполнения». - Не могли бы вы привести пример? Что именно будет обозначать этот другой тег? Структурный тег? Но разве структурные теги не требуют наличия ключевого слова struct перед определением переменной этой структуры? В противном случае мы получили бы ошибку компилятора. Как это вызывает UB?

RobertS supports Monica Cellio 11.02.2020 14:28

@cschol Я бы порекомендовал вам удалить этот ответ или значительно изменить его, потому что он просто неправильный (или, по крайней мере, больше не правильный). Ни на C, ни на C++ вы не получите скомпилированный код первого примера. Я тестировал его с помощью clang и gcc, здесь: godbolt.org/z/Moskd4, и даже с их более старыми версиями. Не только один раз это проходило без хотя бы одной ошибки.

RobertS supports Monica Cellio 11.02.2020 14:48

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

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

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

Иметь самодостаточные структуры совсем несложно. struct foo {struct foo * следующий; int вещь; }

Bernd Jendrissek 30.03.2015 21:52

...какие? Сказать препроцессор для описания разрешения structs и typedefs достаточно плохо, но остальная часть вашего письма настолько сбивает с толку, что мне трудно получить от него какое-либо сообщение. Однако я могу сказать одну вещь: ваша идея о том, что не-typedefd struct нельзя объявлять вперед или использовать в качестве непрозрачного (указателя) члена, полностью ложна. В вашем 1-м примере struct b может тривиально содержать struct a *, typedef не требуется. Утверждения о том, что struct являются просто тупыми частями макрорасширения и что typedef наделяют их новыми революционными возможностями, до боли неверны.

underscore_d 09.04.2016 19:44

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

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

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

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

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

GManNickG 29.05.2010 11:14

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

Bernd Jendrissek 26.11.2012 07:59

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

supercat 27.03.2015 21:54

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

Bernd Jendrissek 29.03.2015 20:16

@BerndJendrissek: Если бы переменные, идентифицирующие файлы, имели тип FILE, а не FILE*, было бы неясно, когда / должны ли методы использовать параметры типа FILE по сравнению с FILE*. В Паскале обычным режимом использования было бы наличие переменных самого типа, но при этом методы принимали бы параметры этого типа по ссылке; C не имеет параметров по ссылке, хотя [я смутно припоминаю, что Паскаль не позволяет передавать переменные типа File или Text по значению, таким образом избегая какой-либо двусмысленности относительно того, что это должно означать].

supercat 29.03.2015 20:36

@BerndJendrissek: Кстати, мне лично интересно, предлагают ли типы объединения какие-либо преимущества, будь то удобство для программистов или простота компилятора, по сравнению с наличием средств явного псевдонима элементов структуры? Я бы подумал, что механизм псевдонима будет проще реализовать для компилятора и более полезен для программистов, особенно если бы в него были включены битовые поля, например int mode_settings; unsigned mode @mode_settings.0:4; unsigned enable @mode_settings.31:1;. Я не знаю, используют ли в настоящее время какие-либо компиляторы @внутри структур таким образом, чтобы это противоречило этому, но ...

supercat 29.03.2015 20:45

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

supercat 29.03.2015 20:48

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

Bernd Jendrissek 30.03.2015 21:47

@BerndJendrissek: Проблема с переносом нотации struct заключается в том, что во многих случаях реализация должна иметь возможность использовать union, а не struct, без необходимости заботы клиентского кода. Если бы C не включил типы union, но вместо этого включил средства явного псевдонима полей структуры, то это не было бы проблемой, но поскольку это типы struct и union, различны. Клиентский API должен заботиться о том, чтобы FILE не определялся типом как массив (они ведут себя странно), но в остальном не должно заботиться о том, что это такое.

supercat 30.03.2015 22:18

@supercat Это интересный момент, и я согласен, только теоретически вы можете обернуть объединение внутри структуры, верно? (Структура с одним членом объединения) И, таким образом, вы не измените публичный интерфейс своей новой реализации.

David Callanan 25.12.2020 14:04

@DavidCallanan: С момента написания вышеизложенного я перешел к использованию синтаксиса struct tag и больше не использую структуры typedef. Решающим фактором был тот факт, что если у кого-то есть заголовок widget.h с функцией int importWidgetFromWazzle(struct widget *dest, struct wazzle *src);, но не все клиенты используют wazzles, использование синтаксиса структурных тегов позволит избежать необходимости включать код клиента wazzle.h, размещать wazzle.h там, где widget.h может его найти. , или займитесь другой гимнастикой. Если бы в C было указано всегда, typedef struct foo foo; будет проигнорирован, если foo уже определен ...

supercat 25.12.2020 20:00

... таким образом библиотека Wazzle могла бы указать, что клиенты могут на досуге содержать typedef struct wazzle wazzle. Я думаю, что полезность этого подсказала мне автономная реализация, которая включает реализацию fprintf и содержит typedef struct __FILE FILE;, но не включает никаких определений для fwrite или типа структуры. Таким образом, пользовательский код может определить struct __FILE, чтобы он содержал все элементы, которые потребуются его реализации fwrite. Если бы в стандарте stdio.h использовался тип struct FILE* вместо FILE, ...

supercat 25.12.2020 20:08

... это обеспечило бы естественный способ для автономных реализаций, позволяющий пользовательскому коду определять свой собственный тип файла, который мог бы работать с fprintf, без необходимости использовать зарезервированный идентификатор для тега файла, создавая потенциальную несовместимость между реализациями, которые выбирают структурный тег __FILE и те, которые случайно выбирают другой структурный тег (например, _FILE).

supercat 25.12.2020 20:11

Удивительно, как много людей ошибаются. ПОЖАЛУЙСТА, не определяйте типы структур в C, это без нужды загрязняет глобальное пространство имен, которое, как правило, уже сильно загрязнено в больших программах на C.

Кроме того, структуры typedef без имени тега являются основной причиной ненужного установления отношений упорядочения между файлами заголовков.

Рассматривать:

#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.

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

William Pursell 25.01.2012 18:04

Питер ван дер Линден также выдвигает аргументы против определения типов структур в своей поучительной книге «Экспертное программирование на языке Си - секреты глубинного языка Си». Суть такова: вы ХОТИТЕ знать, что что-то является структурой или объединением, а не СКРЫТЬ это.

Jens 14.03.2012 14:31

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

jasso 10.12.2012 02:39

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

Pavel Minaev 20.03.2013 11:18

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

Nathan Day 01.06.2013 14:26

В чем должна быть проблема с foo.h в этом примере? Вы все еще можете получить доступ к FOO_DEF, если вы перешли на typedef struct { struct bar *bar; } foo;

M.M 06.11.2014 04:53

@MattMcNabb Я тоже этого не понимаю, ты разобрался?

Janus Troelsen 15.02.2015 00:53

@JanusTroelsen Да, теперь я понимаю: он должен отличаться от struct foo { Bar *bar; };, который требует, чтобы foo.h включал bar.h.

M.M 15.02.2015 00:54

эта строка: '; typedef struct Point Point;' ясно делает мою «точку» о том, чтобы не определять тип структуры. Первая точка - это имя тега структуры. Вторая «Точка» - это «новый» тип. Теперь вам, компилятору и всем, кто читает код, необходимо отслеживать, какая «точка» имеется в виду.

user3629249 28.05.2015 23:43

@ user3629249 это не должно быть слишком сложно. Первому всегда будет предшествовать ключевое слово struct.

Yawar 04.12.2016 08:43

«Какие именно преимущества дает многократный ввод« struct »?» --- Сохраняет пальцы здоровыми благодаря большому количеству упражнений.

Daniel 22.02.2017 21:34

Извините, я добавил -1 :(, так как typedef-ing не предотвращает ничего хорошего, о чем вы упомянули, например, в заголовке opaque.h: typedef struct Opaque Opaque; extern Opaque *OpConstructor (void); extern void OpDestructor (Opaque *);, затем в opaque.c: struct Opaque { actual definition };

Lorinczy Zsigmond 04.05.2017 13:43

Ах да, очень читаемый struct foo *foo;. Иногда я задаюсь вопросом о C-кодерах.

CivFan 02.09.2020 00:50

@LorinczyZsigmond: если два заголовка содержат typedef struct Opaque Opaque;, код, который включает оба, будет несовместим со старыми компиляторами, которые доказали свою надежность, если заголовки не координируют использование макроса #define (что может способствовать еще большему загрязнению пространства имен), чтобы избежать дублирования определения .

supercat 25.12.2020 20:29

@CivFan: если функция использует по одному объекту, каждый из более чем одного типа структуры, и их использование не соответствует никакому четкому шаблону, например «источник» и «место назначения», какие имена вы бы предпочли бы вместо этого? Конечно, можно было бы использовать что-то вроде struct foo *theFoo;, но действительно ли это проясняет что-то более ясное, чем было бы иначе? Идентификатор, который выходит из препроцессора, которому предшествует зарезервированное слово struct, не может быть ничем иным, кроме тега структуры, поэтому я не вижу оснований для путаницы.

supercat 25.12.2020 20:35

@supercat очень верно, этот typedef принадлежит к opaque.h, который должен использовать включить охранников.. Другие заголовки должны либо включать opaque.h, либо использовать формат struct Opaque.

Lorinczy Zsigmond 27.12.2020 12:47

@LorinczyZsigmond: Если некоторые потребители woozle.h будут использовать функцию, которая принимает struct Opaque*, а некоторые нет, те, которые не используют эту функцию, не должно быть opaque.h где-нибудь в своем проекте. Кроме того, если opaque.h включает функцию, которая принимает struct woozle*, попытка обработки определений типов в каждом заголовке до того, как прототипы в другом, потребует координации имен защитных макросов между авторами двух библиотек, при этом просто используя типы struct opaque* и struct woozle* в прототипы избавят от необходимости иметь предварительные определения.

supercat 27.12.2020 21:31

@supercat Я бы посоветовал struct Foo *foo, но да, ваш пример тоже подойдет и будет намного понятнее, чем struct foo *foo.

CivFan 05.01.2021 02:01

typedef, как и определение, - плохая практика, за исключением случаев крайней необходимости. Я видел злоупотребление подобными вещами, сделав невозможным отслеживание кода (typedef в typedef в typedef в ..., определить в определить в определить в ...)

Charlie 13.01.2021 06:53

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

Please don't use things like "vps_t".

It's a mistake to use typedef for structures and pointers. When you see a

vps_t a;

in the source, what does it mean?

In contrast, if it says

struct virtual_container *a;

you can actually tell what "a" is.

Lots of people think that typedefs "help readability". Not so. They are useful only for:

(a) totally opaque objects (where the typedef is actively used to hide what the object is).

Example: "pte_t" etc. opaque objects that you can only access using the proper accessor functions.

NOTE! Opaqueness and "accessor functions" are not good in themselves. The reason we have them for things like pte_t etc. is that there really is absolutely zero portably accessible information there.

(b) Clear integer types, where the abstraction helps avoid confusion whether it is "int" or "long".

u8/u16/u32 are perfectly fine typedefs, although they fit into category (d) better than here.

NOTE! Again - there needs to be a reason for this. If something is "unsigned long", then there's no reason to do

typedef unsigned long myflags_t;

but if there is a clear reason for why it under certain circumstances might be an "unsigned int" and under other configurations might be "unsigned long", then by all means go ahead and use a typedef.

(c) when you use sparse to literally create a new type for type-checking.

(d) New types which are identical to standard C99 types, in certain exceptional circumstances.

Although it would only take a short amount of time for the eyes and brain to become accustomed to the standard types like 'uint32_t', some people object to their use anyway.

Therefore, the Linux-specific 'u8/u16/u32/u64' types and their signed equivalents which are identical to standard types are permitted -- although they are not mandatory in new code of your own.

When editing existing code which already uses one or the other set of types, you should conform to the existing choices in that code.

(e) Types safe for use in userspace.

In certain structures which are visible to userspace, we cannot require C99 types and cannot use the 'u32' form above. Thus, we use __u32 and similar types in all structures which are shared with userspace.

Maybe there are other cases too, but the rule should basically be to NEVER EVER use a typedef unless you can clearly match one of those rules.

In general, a pointer, or a struct that has elements that can reasonably be directly accessed should never be a typedef.

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

Yawar 04.12.2016 08:48

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

Baldrickk 10.01.2017 19:18

@Yawar: Думаю, дело было «в самих себе». Предположим, у кого-то есть тип, который должен представлять трехмерную точку с использованием координат float. Можно потребовать, чтобы код, который хочет прочитать значение x точки, использовал для этого функцию доступа, и бывают случаи, когда действительно абстрактный тип «читаемой трехмерной точки» может быть полезен, но есть много других случаев, когда это необходимо это тип, который может делать все, что может делать тройка float x,y,z, с той же семантикой. В последних ситуациях попытка сделать шрифт непрозрачным приведет к путанице, а не к ясности.

supercat 25.12.2020 20:46

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

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

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

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

unsigned X x; but not
unsigned Y y;

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

MyPipe *s; MyWriter *self = (MyWriter *) s;, и вы только что нарушили строгий алиасинг.
Jonathon Reinhart 01.06.2015 22:18

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

underscore_d 09.04.2016 19:35

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

Qwertyzw 11.05.2018 16:39

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;

Какой в ​​этом смысл?

Если бы в стандарте C всегда разрешалось повторение определений имен типов в тех случаях, когда старые и новые определения точно совпадают, я бы предпочел, чтобы код, который будет использовать тип структуры, включал typedef struct name name; как обычную практику, но из старых компиляторов которые зарекомендовали себя надежными, не допускают таких повторяющихся определений, и обход этого ограничения создает больше проблем, чем простое использование struct tagName в качестве имени типа.

supercat 25.12.2020 20:25

В языке программирования C ключевое слово typedef используется для объявления нового имени для некоторого объекта (структура, массив, функция..тип enum). Например, я буду использовать "struct-s". В C мы часто объявляем структуру вне функции 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' 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);

}

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

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

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

"Оказывается, в C99 требуется typedef". Что ты имеешь в виду?

Julien Lopez 02.11.2016 12:54

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

yyny 01.12.2016 00:26

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

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

C> Определение типа структуры объясняется в A выше.

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

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

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

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

typedef point { ... } Points - не самая лучшая идея. Поскольку названия point и Points имеют слишком большую разницу. Когда кому-то нужно переслать объявление struct для указателя, тогда другое имя становится проблемой, особенно когда оно находится в библиотеке, которая может измениться. Если вы используете typedef, используйте то же имя или четкое правило, как преобразовать имя из имени struct в имя typedef.

12431234123412341234123 07.09.2020 12:17

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