typedef struct vs struct определений

670

Я новичок в программировании на С, но мне было интересно узнать, какая разница между использованием typedef при определении структуры или использованием typedef. Мне кажется, что нет никакой разницы, они достигают той же цели.

struct myStruct{
    int one;
    int two;
};

против

typedef struct{
    int one;
    int two;
}myStruct;
  • 0
    Я только что прочитал здесь, на SO, что второй вариант даст ошибку компилятора ?! «передаваемый аргумент несовместимого типа указателя» stackoverflow.com/questions/12708897/…
Теги:
struct
typedef

12 ответов

890
Лучший ответ

Общая идиома использует оба:

typedef struct X { 
    int x; 
} X;

Это разные определения. Чтобы сделать обсуждение более ясным, я разделил предложение:

struct S { 
    int x; 
};

typedef struct S S;

В первой строке вы определяете идентификатор S в пространстве имен структуры (не в смысле С++). Вы можете использовать его и определить переменные или аргументы функции только что определенного типа, указав тип аргумента как struct S:

void f( struct S argument ); // struct is required here

Вторая строка добавляет псевдоним типа S в глобальное пространство имен и, таким образом, позволяет вам просто написать:

void f( S argument ); // struct keyword no longer needed

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

Чтобы сделать разницу более четкой:

typedef struct S { 
    int x; 
} T;

void S() { } // correct

//void T() {} // error: symbol T already defined as an alias to 'struct S'

Вы можете определить функцию с тем же именем структуры, что и идентификаторы, хранятся в разных пространствах, но вы не можете определить функцию с тем же именем, что и typedef, поскольку эти идентификаторы сталкиваются.

В С++ он немного отличается, поскольку правила поиска символа изменились тонко. С++ все еще сохраняет два разных пространства идентификаторов, но в отличие от C, когда вы определяете только символ в пространстве идентификаторов класса, вам не требуется указывать ключевое слово struct/class:

 // C++
struct S { 
    int x; 
}; // S defined as a class

void f( S a ); // correct: struct is optional

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

Представленный выше код ведет себя одинаково:

typedef struct S { 
    int x; 
} T;

void S() {} // correct [*]

//void T() {} // error: symbol T already defined as an alias to 'struct S'

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

// previous code here...
int main() {
    S(); 
    struct S s;
}
  • 26
    Хороший ответ также показывает мне, почему я хочу ввести определение, поэтому его нельзя перезаписать как функцию, спасибо :)
  • 9
    @AlexanderVarwijk: Вы хотите ввести typedef чтобы избежать необходимости квалифицироваться с struct или enum . Если используемые вами соглашения об именах допускают использование функции и типа с одним и тем же именем, лучшее, что вы можете сделать, это проверить, как вы называете элементы своей программы.
Показать ещё 14 комментариев
90

struct и typedef - две разные вещи.

Ключевое слово struct используется для определения или ссылки на тип структуры. Например, это:

struct foo {
    int n;
};

создает новый тип, называемый struct foo. Имя foo - это тег; он имеет смысл только тогда, когда ему сразу предшествует ключевое слово struct, потому что теги и другие идентификаторы находятся в разных пространствах имен. (Это похоже на, но гораздо более ограниченное, чем концепция С++ namespace s.)

A typedef, несмотря на имя, не определяет новый тип; он просто создает новое имя для существующего типа. Например, данный:

typedef int my_int;

my_int - новое имя для int; my_int и int являются точно такими же. Аналогично, учитывая определение struct выше, вы можете написать:

typedef struct foo foo;

Тип уже имеет имя struct foo. Объявление typedef дает тот же тип новому имени, foo.

Синтаксис позволяет объединить struct и typedef в одно объявление:

typedef struct bar {
    int n;
} bar;

Это обычная идиома. Теперь вы можете ссылаться на этот тип структуры либо как struct bar, либо как bar.

Обратите внимание, что имя typedef не становится видимым до конца объявления. Если структура содержит указатель на себя, вы используете версию struct для ссылки на нее:

typedef struct node {
    int data;
    struct node *next; /* can't use just "node *next" here */
} node;

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

typedef struct node_s {
    /* ... */
} node;

(Лично я предпочитаю пропускать typedef и ссылаюсь на тип как struct bar. typedef сохраняет немного ввода, но скрывает тот факт, что это тип структуры. Если вы хотите, чтобы тип быть непрозрачным, это может быть хорошо. Если код клиента будет ссылаться на член n по имени, то он не непрозрачен, это явно структура, и, на мой взгляд, имеет смысл ссылаться на нее как на но многие умные программисты не согласны со мной в этом вопросе. Будьте готовы читать и понимать код, написанный в любом случае.)

(С++ имеет разные правила. Учитывая объявление struct blah, вы можете ссылаться на тип как на blah, даже без typedef. Использование typedef может сделать ваш C-код немного более похожим на С++ - если вы думаете, что это хорошо.)

  • 3
    Этот ответ помог мне лучше понять, почему библиотеки C89 и ядро Linux чаще используют struct mystruct_t {} а не typedef struct {} mystruct_t .
81

Другое отличие, которое не указано, заключается в том, что предоставление структуры имени (т.е. struct myStruct) также позволяет вам предоставлять декларации вперед структуры. Поэтому в другом файле вы можете написать:

struct myStruct;
void doit(struct myStruct *ptr);

не имея доступа к определению. Я рекомендую вам объединить два примера:

typedef struct myStruct{
    int one;
    int two;
} myStruct;

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

  • 1
    Есть способы сделать typedef struct myStruct myStruct; объявления с помощью typedef: typedef struct myStruct myStruct; ; а затем (позже): struct myStruct { ... }; , Вы можете использовать его либо как struct myStruct или просто myStruct после этого typedef (но тип не является неполным , пока это определение).
  • 0
    Стоит также упомянуть, что C ++, несмотря на (концептуально) теги автоматического определения типа и помещение обоих struct SomeThing и Something в единое «пространство символов», явно typedef SomeThing ручное определение типа typedef SomeThing для переопределения в struct SomeThing ... где в противном случае вы можете предположить, что это приведет к ошибке при конфликте имен. Источник: stackoverflow.com/a/22386307/2757035 Я полагаю, это было сделано по (немного бесполезным!) Причинам обратной совместимости.
Показать ещё 1 комментарий
52

В C (не С++) вы должны объявить структурные переменные, например:

struct myStruct myVariable;

Чтобы использовать myStruct myVariable; вместо этого, вы можете typedef struct:

typedef struct myStruct someStruct;
someStruct myVariable;

Вы можете объединить определение struct и typedef его в один оператор, объявляющий анонимные struct и typedef его.

typedef struct { ... } myStruct;
  • 1
    Последний блок кода не эквивалентен предыдущему коду. В последней строке вы определяете псевдоним типа myStruct в безымянную структуру. Есть (очень) тонкая разница между двумя версиями.
  • 4
    dribeas: я рассмотрел эту тонкую разницу в предложении «... единственное утверждение, которое объявляет анонимную структуру и ...»
Показать ещё 6 комментариев
24

Если вы используете struct без typedef, вам всегда придется писать

struct mystruct myvar;

Нелегально писать

mystruct myvar;

Если вы используете typedef, вам больше не нужен префикс struct.

  • 3
    Мой ответ уже не актуален. Текущие компиляторы обрабатывают «typedef struct» и «struct» одинаково. На оба можно ссылаться без префикса "struct". Т.е. VC2013 ведет себя так.
  • 0
    Я ценю ваш короткий ответ, а также другой принятый. Ради порядка, что вы имеете в виду под "Текущими компиляторами"? К какому типу C-версии вы относитесь? Например, я протестировал поведение во время кросс-компиляции с компилятором ARM (C99). В моем случае «typedef struct» и «struct» не рассматриваются как одно и то же.
Показать ещё 1 комментарий
22

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

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

Поэтому рекомендуется (см., например, руководство по стилю ядра ядра Linux, глава 5), чтобы это делать только тогда, когда вы действительно хотите скрыть эту информацию, а не просто сохранить несколько нажатий клавиш.

Примером использования typedef будет непрозрачный тип, который используется только с соответствующими функциями/макросами доступа.

5

Вы не можете использовать форвардное объявление с typedef struct.

Сам struct является анонимным типом, поэтому у вас нет фактического имени для переадресации объявления.

typedef struct{
    int one;
    int two;
} myStruct;

Непосредственное объявление вроде этого не будет работать:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types
5

Разница возникает, когда вы используете struct.

Первый способ, которым вы должны выполнить:

struct myStruct aName;

Второй способ позволяет удалить ключевое слово struct.

myStruct aName;
4

Следующий код создает анонимную структуру с псевдонимом myStruct:

typedef struct{
    int one;
    int two;
} myStruct;

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

4

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

struct myStruct blah;

против.

myStruct blah;
3

Я вижу, что в этом есть некоторые разъяснения. C и С++ не определяют типы по-разному. С++ первоначально был не чем иным, как дополнительным набором включений поверх C.

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

Единственная причина, по которой такие объявления и определения существуют, заключается в том, что компоновщик может вычислять смещения адресов для полей в структуре. Вот почему большинство людей уходит с кодом, который на самом деле написан неправильно - потому что компилятор способен определить адресацию. Проблема возникает, когда кто-то пытается сделать что-то заранее, например, в очереди или связанном списке, или копируя структуру O/S.

Объявление начинается с 'struct', определение начинается с 'typedef'.

Кроме того, структура имеет знак прямого декларирования и определенную метку. Большинство людей этого не знают и используют ярлык прямого объявления как метку define.

Неправильно:

struct myStruct
   {
   int field_1;
   ...
   };

Они просто использовали объявление forward для маркировки структуры - так что теперь компилятор знает об этом, но он не является определенным определенным типом. Компилятор может вычислить адресацию - но это не то, как оно предназначалось для использования, по причинам, которые я покажу на мгновение.

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

Вместо этого любая структура, которая не ссылается на себя, должна быть объявлена ​​и определена только так:

typedef struct
   {
   field_1;
   ...
   }myStruct;

Теперь это фактический тип, и при использовании вы можете использовать его как "myStruct", не добавляя его со словом "struct".

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

typedef struct
   {
   field_1;
   ...
   }myStruct,*myStructP;

Теперь у вас есть указательная переменная для этой структуры, настраиваемая для нее.

ПЕРЕДНЯЯ ДЕКЛАРАЦИЯ -

Теперь, вот фантастический материал, как работает форвардная декларация. Если вы хотите создать тип, который ссылается на себя, например связанный список или элемент очереди, вам нужно использовать декларацию forward. Компилятор не рассматривает структуру, определенную до тех пор, пока она не дойдет до точки с запятой в самом конце, поэтому она просто объявлена ​​до этой точки.

typedef struct myStructElement
   {
   myStructElement*  nextSE;
   field_1;
   ...
   }myStruct;

Теперь, компилятор знает, что, хотя он еще не знает, что такое весь тип, он все равно может ссылаться на него с помощью прямой ссылки.

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

  • 4
    Я не верю, что C ++ когда-либо был «набором включений поверх C». Первая реализация, cfront была препроцессором, который переводил исходный код C ++ в исходный код C. И я не согласен с вашим советом об использовании typedef для структур, и особенно для указателей. Скрывать тип указателя за typedef может быть опасно; ИМХО лучше сделать так, чтобы это был явный указатель, используя * . В вашем последнем примере мне любопытно обосновать использование одного имени ( myStruct ) для typedef и другого ( myStructElement ) для тега struct.
  • 2
    Во многом это вопрос стилистических предпочтений. Я довольно сильно не согласен с большинством ваших предложений (это не значит, что вы не правы). Смотрите мой ответ для краткого изложения моих собственных предпочтений (которые разделяют многие, но, безусловно, не все программисты на C).
1

В последнем примере вы опускаете ключевое слово struct при использовании структуры. Поэтому везде в вашем коде вы можете написать:

myStruct a;

вместо

struct myStruct a;

Это сохранит некоторые типизации и может быть более читаемым, но это вопрос вкуса

Ещё вопросы

Сообщество Overcoder
Наверх
Меню