typedef — зарезервированное ключевое слово в языках программирования C , C++ и Objective-C . Он используется для создания дополнительного имени ( псевдонима ) для другого типа данных , но не создает новый тип, [1] за исключением неясного случая квалифицированного определения типа типа массива, когда квалификаторы typedef передаются элементу массива. тип. [2] Таким образом, он часто используется для упрощения синтаксиса объявления сложных структур данных , состоящих из типов struct и Union , хотя он также часто используется для предоставления конкретных описательных имен типов для целочисленных типов данных различных размеров. [1]
Объявление typedef следует тому же синтаксису, что и объявление любого другого идентификатора C. Ключевое слово typedef
само по себе является спецификатором, а это означает, что, хотя оно обычно появляется в начале объявления, оно также может появляться после спецификаторов типа или между двумя из них. [3] [4]
В стандартной библиотеке C и в спецификациях POSIX идентификатор определения typedef часто сопровождается суффиксом _t
, например, in size_t
и time_t
. Это практикуется в других системах кодирования, хотя POSIX явно резервирует эту практику для типов данных POSIX.
Это создает тип length
как синоним type int
:
typedef int длина ;
Объявление typedef может использоваться в качестве документации, указывая значение переменной в контексте программирования, например, оно может включать выражение единицы измерения или количества. Общие объявления,
интервал текущей_скорости ; int high_score ; void поздравлять ( int your_score ) { if ( your_score > high_score ) { // ... } }
может быть выражено путем объявления типов, специфичных для контекста:
typedef int km_per_hour ; typedef int точек ; // `km_per_hour` здесь является синонимом `int`, поэтому компилятор обрабатывает // наши новые переменные как целые числа. км_в_час текущая_скорость ; очки high_score ; void поздравлять ( баллы your_score ) { if ( your_score > high_score ) { // ... } }
Обе части кода выполняются одинаково. Однако использование объявлений typedef во втором блоке кода проясняет, что две переменные, хотя и представляют один и тот же тип данных int
, хранят разные или несовместимые данные. Определение congratulate()
of your_score
указывает программисту, что current_speed
(или любую другую переменную, не объявленную как a points
) не следует передавать в качестве аргумента. Это не было бы так очевидно, если бы оба были объявлены как переменные int
типа данных. Однако это указание предназначено только для программиста ; Компилятор C/C++ считает, что обе переменные относятся к определенному типу, int
и не помечает предупреждения о несоответствии типов или ошибки для «неправильных» типов аргументов congratulate(points your_score)
в приведенном ниже фрагменте кода:
void foo () { km_per_hour km100 = 100 ; поздравить ( км100 ); }
Typedef может использоваться для упрощения объявлений объектов, имеющих типы с подробными именами, такие как struct , Union или типы указателей . [5] Например,
struct MyStruct { int data1 ; символьные данные2 ; };
определяет тип данных struct MyStruct
. В C, при отсутствии typedef, для объявления переменных этого типа необходимо использовать полное имя типа:
структура MyStruct a ;
Объявление typedef может предоставить более простое имя типа, не включающее struct
. Например, с объявлением типа
typedef struct MyStruct newtype ;
объявление переменной можно свести к:
новый тип а ;
Объявление структуры и typedef также можно объединить в одно объявление:
typedef struct MyStruct { int data1 ; символьные данные2 ; } Новый тип ;
, в том числе когда тег не объявлен:
typedef struct { int data1 ; символьные данные2 ; } Новый тип ;
В C++ , в отличие от C, теги class
, struct
, union
и enum
типов (такие как " MyStruct
" выше) автоматически могут использоваться сами по себе в качестве псевдонимов для полных имен типов, очень похоже на то, как если бы были явные определения типов, объявляющие это в момент объявление типа:
структура MyStruct х ; МояСтруктура y ;
Действительно, для class
типов struct
и union
C++ называет тег именем класса.
Соответствие между этой функцией C++ и typedef очень сильное, вплоть до того, что можно скрыть имя простого типа во вложенной области, объявив его как идентификатор объекта другого типа. В таком случае полное имя типа в стиле C («проработанный спецификатор типа») по-прежнему может использоваться для ссылки на класс или тип перечисления.
Таким образом, в C++ MyStruct
его можно использовать где угодно newtype
, если только нет других объявлений этих идентификаторов. Однако обратное неверно, поскольку C++ требует имя класса для некоторых целей, например, для имен методов конструктора.
Печально известный пример, когда даже в C++ требуется это struct
ключевое слово, — это системный вызов POSIX stat , который использует в своих аргументах структуру с тем же именем:
int stat ( const char * имя файла , struct stat * buf ) { // ... }
Здесь как C , так и C++ требуется struct
ключевое слово в определении параметра. [ сомнительно ]
Typedef может использоваться для определения нового типа указателя.
typedef int * intptr ; intptr ПТР ; // То же, что: // int *ptr;
intptr
— это новый псевдоним с типом указателя int *
. Определение intptr ptr;
определяет переменную ptr
типа int *
. Итак, ptr
это указатель, который может указывать на переменную типа int
.
Использование typedef для определения нового типа указателя иногда может привести к путанице. Например:
typedef int * intptr ; // И «клифф», и «аллен» имеют тип int*. intptr Клифф , Аллен ; // «cliff2» имеет тип int*, а «allen2» имеет тип int**. intptr clip2 , * allen2 ; // То же, что: // intptr clip2; // интервал *allen2;
Выше intptr cliff, allen;
означает определение двух переменных с int*
типом для обеих. Это связано с тем, что тип, определенный с помощью typedef, является типом, а не расширением. Другими словами, intptr
, который является int*
типом, украшает cliff
и allen
. Для intptr cliff2, *allen2;
тип intptr
украшает cliff2
и *allen2
. Итак, intptr cliff2, *allen2;
эквивалентно двум отдельным определениям intptr cliff2;
и intptr *allen2
. intptr *allen2
означает, что allen2
это указатель, указывающий на память определенного int*
типа. Короче говоря, allen2
имеет тип int**
.
Опять же, поскольку typedef определяет тип, а не расширение, объявления, использующие квалификатор const, могут давать неожиданные или неинтуитивные результаты. В следующем примере объявляется постоянный указатель на целочисленный тип, а не указатель на постоянное целое число:
typedef int * intptr ; const intptr ptr = NULL ; // То же, что: // int *const ptr = NULL;
Поскольку это указатель на константу, его необходимо инициализировать в объявлении.
Typedefs также могут упростить определения или объявления типов указателей на структуры . Учти это:
структура Node { интервал данных ; struct Node * nextptr ; };
Используя typedef, приведенный выше код можно переписать следующим образом:
typedef struct Node Node ; структура Node { интервал данных ; Узел * следующийптр ; };
В C можно объявить несколько переменных одного типа в одном операторе, даже смешивая структуру с указателями или без указателей. Однако перед каждой переменной нужно будет поставить звездочку, чтобы обозначить ее как указатель. В дальнейшем программист может предположить, что errptr
это действительно файл Node *
, но опечатка означает, что errptr
это файл Node
. Это может привести к тонким синтаксическим ошибкам.
struct Node * startptr , * endptr , * curptr , * prevptr , errptr , * refptr ;
Определив typedef Node *
, можно гарантировать, что все переменные являются типами указателей на структуру или, скажем, что каждая переменная является типом указателя , указывающим на тип структуры .
typedef struct Node * NodePtr ; NodePtr startptr , endptr , curptr , prevptr , errptr , refptr ;
int do_math ( float arg1 , int arg2 ) { return arg2 ; } int call_a_func ( int ( * call_this ) ( float , int )) { int output = call_this ( 5.5 , 7 ); возвратный вывод ; } int Final_result = call_a_func ( & do_math );
Приведенный выше код можно переписать со спецификациями typedef:
typedef int ( * MathFunc ) ( float , int ); int do_math ( float arg1 , int arg2 ) { return arg2 ; } int call_a_func ( MathFunc call_this ) { int output = call_this ( 5.5 , 7 ); возвратный вывод ; } int Final_result = call_a_func ( & do_math );
Вот MathFunc
новый псевдоним для типа. A MathFunc
— это указатель на функцию, которая возвращает целое число и принимает в качестве аргументов число с плавающей запятой, за которым следует целое число.
Когда функция возвращает указатель на функцию , без typedef ситуация может быть еще более запутанной. Ниже приведен прототип функции signal(3) из FreeBSD :
void ( * signal ( int sig , void ( * func )( int )))( int );
Приведенное выше объявление функции является загадочным, поскольку оно не ясно показывает, что функция принимает в качестве аргументов или тип, который она возвращает. Начинающий программист может даже предположить, что функция принимает int
в качестве аргумента единицу и ничего не возвращает, но на самом деле ей также нужен указатель на функцию, и она возвращает указатель на другую функцию. Можно написать более чисто:
typedef void ( * sighandler_t )( int ); сигнал Sigsandler_t ( int sig , Signandler_t Func );
Typedef также можно использовать для упрощения определения типов массива. Например,
typedef char arrType [ 6 ]; arrType arr = { 1 , 2 , 3 , 4 , 5 , 6 }; arrType * pArr ; // То же, что: // char arr[6] = {1, 2, 3, 4, 5, 6}; // символ (*pArr)[6];
Вот arrType
новый псевдоним типа char[6]
, который представляет собой массив из 6 элементов. For arrType *pArr;
— pArr
указатель, указывающий на память типа char[6]
.
Typedef создается с использованием синтаксиса определения типа , но может использоваться так, как если бы он был создан с использованием синтаксиса приведения типов . ( Приведение типов изменяет тип данных.) Например, в каждой строке после первой строки:
// funcptr — это указатель на функцию, которая принимает double и возвращает int. typedef int ( * funcptr ) ( двойной ); // Действительно как в C, так и в C++. funcptr x = ( funcptr ) NULL ; // Действует только в C++. funcptr y = funcptr ( NULL ); funcptr z = static_cast < funcptr > ( NULL );
funcptr
используется слева для объявления переменной и справа для приведения значения. Таким образом, typedef может использоваться программистами, которые не хотят разбираться, как преобразовать синтаксис определения в синтаксис приведения типа.
Без typedef обычно невозможно использовать синтаксис определения и синтаксис приведения как взаимозаменяемые. Например:
пустота * р = NULL ; // Это законно. int ( * x )( double ) = ( int ( * ) ( double )) p ; // Левая часть недопустима. int ( * )( double ) y = ( int ( * ) ( double )) p ; // Правая часть недопустима. int ( * z )( double ) = ( int ( * p )( double ));
В C++ имена типов могут быть сложными, и typedef предоставляет механизм присвоения типам простого имени.
std :: vector < std :: pair < std :: string , int >> значения ; for ( std :: vector < std :: pair < std :: string , int >>:: const_iterator i = Values.begin ( ) ; i ! = Values.end ( ); ++ i ) { std :: pair < std :: string , int > const & t = * i ; // ... }
и
typedef std :: pair < std :: string , int > value_t ; typedef std :: vector < value_t > values_t ; значения_t значения ; for ( values_t :: const_iterator i = Values.begin ( ); i ! = Values.end ( ) ; ++ i ) { value_t const & t = * i ; // ... }
В C++11 появилась возможность выражать определения типов using
вместо typedef
. Например, два приведенных выше определения типов можно эквивалентно записать как
используя value_t = std :: pair < std :: string , int > ; использование значений_t = std :: vector <value_t> ;
C++03 не предоставляет шаблонных определений типов. Например, чтобы иметь stringpair<T>
представление std::pair<std::string, T>
для каждого типа, нельзяT
использовать :
шаблон < имя типа T > typedef std :: pair < std :: string , T > stringpair <T> ; // Не работает
Однако, если кто-то готов принять stringpair<T>::type
вместо stringpair<T>
, то можно добиться желаемого результата с помощью typedef внутри неиспользуемого в противном случае шаблонного класса или структуры:
template < typename T > class stringpair { Private : // Предотвращаем создание экземпляра `stringpair<T>`. пара строк (); public : // Сделать `stringpair<T>::type` представляющим `std::pair<std::string, T>`. typedef std :: pair < std :: string , T > type ; }; // Объявляем переменную типа `std::pair<std::string, int>`. stringpair < int >:: type my_pair_of_string_and_int ;
В C++11 шаблонные определения типов добавляются со следующим синтаксисом, который требует using
ключевого слова, а не typedef
ключевого слова. (См. псевдонимы шаблонов .) [6]
шаблон < имя типа T > с использованием stringpair = std :: pair < std :: string , T > ; // Объявляем переменную типа `std::pair<std::string, int>`. stringpair < int > my_pair_of_string_and_int ;
В SystemVerilog typedef ведет себя точно так же, как в C и C++. [7]
Во многих статически типизированных функциональных языках, таких как Haskell , Miranda , OCaml и т. д., можно определять синонимы типов , которые совпадают с определениями типов в C. Пример в Haskell:
введите PairOfInts = ( Int , Int )
В этом примере синоним типа определен PairOfInts
как целочисленный тип.
В Seed7 определение постоянного типа используется для введения синонима типа:
тип const: myVector — целое число массива;
В Swifttypealias
для создания typedef используется ключевое слово:
typealias PairOfInts = ( Int , Int )
C# содержит функцию, аналогичную typedef или синтаксису using
C++. [8] [6]
используя newType = global :: System . Время выполнения . Взаимодействие . Маршал ; используя другойТип = Перечисления . МойЕнумТип ; используя StringListMap = System . Коллекции . Общий . Словарь < строка , System . Коллекции . Общий . Список < строка >> ;
В D ключевое слово alias
[9] позволяет создавать синонимы типа или частичного типа.
struct Foo ( T ){} псевдоним FooInt = Foo ! интервал ; псевдоним Fun = int делегат ( int );
Керниган и Ритчи назвали две причины использования typedef. [1] Во-первых, он предоставляет средства, позволяющие сделать программу более переносимой или простой в обслуживании. Вместо того, чтобы менять тип во всех источниках программы, достаточно изменить только один оператор typedef. size_t
и ptrdiff_t
в <stdlib.h>
таких именах typedef. Во-вторых, typedef может облегчить понимание сложного определения или объявления.
Некоторые программисты выступают против широкого использования определений типов. Большинство аргументов основаны на идее, что определения типов просто скрывают фактический тип данных переменной. Например, Грег Кроа-Хартман , хакер ядра Linux и документатор, не рекомендует использовать их ни для чего, кроме объявлений прототипов функций. Он утверждает, что такая практика не только излишне запутывает код, но также может привести к тому, что программисты случайно неправильно используют большие структуры, считая их простыми типами. [10]
C предоставляет возможность typedef для создания имен новых типов данных. … Следует подчеркнуть, что объявление typedef ни в каком смысле не создает новый тип; он просто добавляет новое имя для некоторого существующего типа.
Имена структурных типов часто определяются с помощью typedef для создания более коротких имен типов.
Использование typedef только скрывает реальный тип переменной.