typedef - зарезервированное ключевое слово в языках программирования C и C ++ . Он используется для создания дополнительного имени ( псевдонима ) для другого типа данных , но не создает новый тип, [1] за исключением неясного случая квалифицированного typedef типа массива, где квалификаторы typedef передаются элементу массива тип. [2] Таким образом, он часто используется для упрощения синтаксиса объявления сложных структур данных, состоящих из типов структур и объединений , но столь же распространен при предоставлении конкретных описательных имен типов дляцелочисленные типы данных различной длины .
Синтаксис
Синтаксис объявления typedef: [3]
typedef
тип-объявление ;
Имя нового псевдонима типа следует тому же синтаксису, что и объявление любого другого идентификатора C, поэтому в более подробной форме:
typedef
идентификатор определения типа
В стандартной библиотеке C и в спецификациях POSIX идентификатор для определения typedef часто дополняется суффиксом _t
, например size_t и time_t . Это практикуется в других системах кодирования, хотя POSIX явно резервирует эту практику для типов данных POSIX .
Примеры
typedef int length ;
Это создает тип length
как синоним типа int
.
Использование документации
Объявление typedef может использоваться в качестве документации, указывая значение переменной в контексте программирования, например, оно может включать выражение единицы измерения или количества. Общие объявления,
int current_speed ; int high_score ;void поздравляю ( int your_score ) { if ( your_score > high_score ) { // ... } }
может быть выражено объявлением типов, зависящих от контекста:
typedef int km_per_hour ; typedef int points ;// `km_per_hour` здесь является синонимом` int`, и поэтому компилятор обрабатывает // наши новые переменные как целые числа. km_per_hour current_speed ; очки high_score ;void поздравляю ( баллы your_score ) { if ( your_score > high_score ) { // ... } }
Оба раздела кода выполняются идентично. Однако использование объявлений typedef во втором блоке кода дает понять, что две переменные, представляющие один и тот же тип данных int
, хранят разные или несовместимые данные. Определения в congratulate()
о your_score
указует программист этого current_speed
(или любого другим переменной , не объявленные как points
) не должны быть переданы в качестве аргумента. Это было бы не так очевидно, если бы обе они были объявлены как переменные типа int
данных. Однако это указание только для программиста ; компилятор C / C ++ считает, что обе переменные относятся к типу, int
и не отмечает предупреждения о несоответствии типов или ошибки для «неправильных» типов аргументов congratulate(points your_score)
в приведенном ниже фрагменте кода:
void foo () { km_per_hour km100 = 100 ; поздравить ( км100 ); }
Упрощение типов
Typedef может использоваться для упрощения объявления составного типа ( структура , объединение ) или типа указателя . [4] Например,
struct MyStruct { int data1 ; char data2 ; };
Это определяет тип данных struct MyStruct
. Объявление переменной этого типа в C также требует ключевого слова struct
, но его можно опустить в C ++:
struct MyStruct a ;
Объявление typedef исключает необходимость указания struct
в C. Например, объявление
typedef struct MyStruct newtype ;
сводится к:
newtype a ;
Объявление структуры и typedef также можно объединить в один оператор:
typedef struct MyStruct { int data1 ; char data2 ; } newtype ;
Или его можно использовать следующим образом:
typedef struct { int data1 ; char data2 ; } newtype ;
В C ++ , в отличие от C, ключевые слова struct
, class
и enum
являются необязательными в объявлениях переменных, которые отделены от определений, если нет двусмысленности с другим идентификатором:
struct MyStruct x ; MyStruct y ;
Таким образом, MyStruct
можно использовать везде, где только newtype
можно. Однако обратное неверно; например, методы конструктора для MyStruct
не могут быть названы newtype
.
Печально известный пример, когда даже C ++ нуждается в struct
ключевом слове, - это системный вызов POSIX stat, который использует структуру с тем же именем в своих аргументах:
int stat ( const char * filename , struct stat * buf ) { // ... }
Здесь и C, и C ++ нуждаются в struct
ключевом слове в определении параметра.
Указатели
Typedef может использоваться для определения нового типа указателя.
typedef int * intptr ;intptr ptr ;// То же, что и: // int * ptr;
intptr
это новый псевдоним с типом указателя int *
. Определение intptr ptr;
,, определяет переменную ptr
с типом int *
. Итак, ptr
это указатель, который может указывать на переменную типа int
.
Использование typedef для определения нового типа указателя иногда может привести к путанице. Например:
typedef int * intptr ;// Оба 'cliff' и 'allen' относятся к типу int *. intptr cliff , allen ;// 'cliff2' имеет тип int *, а 'allen2' имеет тип int **. intptr cliff2 , * allen2 ;// То же, что и: // intptr cliff2; // intptr * allen2;
Выше intptr cliff, allen;
означает определение 2 переменных с 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**
.
Структуры и указатели структур
Typedefs также может упростить определения или объявления типов указателей на структуру . Учти это:
struct Node { int data ; struct Node * nextptr ; };
Используя typedef, приведенный выше код можно переписать следующим образом:
typedef struct Node Node ;struct Node { int data ; Узел * nextptr ; };
В 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 ) { вернуть 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 ) { вернуть 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
аргумент и ничего не возвращает, но на самом деле ей также нужен указатель на функцию и она возвращает другой указатель на функцию. Более чисто можно написать:
определение типа во недействительным ( * sighandler_t ) ( INT ); сигнал sighandler_t ( int sig , sighandler_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}; // char (* pArr) [6];
Вот arrType
новый псевдоним для char[6]
типа, который представляет собой тип массива с 6 элементами. Для arrType *pArr;
, pArr
это указатель , указывающий на память char[6]
типа.
Приведение типов
Typedef создается с использованием синтаксиса определения типа, но может использоваться, как если бы он был создан с использованием синтаксиса приведения типов . ( Приведение типов изменяет тип данных.) Например, в каждой строке после первой строки:
// `funcptr` - это указатель на функцию, которая принимает` double` и возвращает `int`. typedef int ( * funcptr ) ( двойной );// Допустимо как для C, так и для C ++. funcptr x = ( funcptr ) NULL ;// Допустимо только в C ++. funcptr y = funcptr ( NULL ); funcptr г = static_cast < funcptr > ( NULL );
funcptr
используется в левой части для объявления переменной и используется в правой части для приведения значения. Таким образом, typedef может использоваться программистами, которые не хотят выяснять, как преобразовать синтаксис определения в синтаксис приведения типов.
Без typedef, как правило, невозможно взаимозаменяемо использовать синтаксис определения и синтаксис приведения. Например:
void * p = NULL ;// Это законно. int ( * x ) ( двойной ) = ( int ( * ) ( двойной )) p ;// Левая часть недопустима. int ( * ) ( двойной ) y = ( int ( * ) ( двойной )) p ;// Правая часть недопустима. int ( * z ) ( двойной ) = ( int ( * p ) ( двойной ));
Использование в C ++
В C ++ имена типов могут быть сложными, а typedef предоставляет механизм для присвоения типу простого имени.
std :: vector < std :: pair < std :: string , int >> values ;for ( std :: vector < std :: pair < std :: string , int >> :: const_iterator i = values . begin (); i ! = values . end (); ++ i ) { std :: pair < std :: string , int > const & t = * я ; // ... }
а также
typedef std :: pair < std :: string , int > value_t ; typedef std :: vector < value_t > values_t ;values_t значения ;for ( values_t :: const_iterator i = values . begin (); i ! = values . end (); ++ i ) { value_t const & t = * i ; // ... }
В C ++ 11 появилась возможность выражать typedef с помощью using
вместо typedef
. Например, два вышеупомянутых typedef могут быть эквивалентно записаны как
используя value_t = std :: pair < std :: string , int > ; using values_t = std :: vector < value_t > ;
Использовать с шаблонами
C ++ 03 не предоставляет шаблонных определений типов. Например, чтобы stringpair
представлять std::pair<:string t="">,>
для каждого типа T
один не может использовать:
template < typename T > typedef std :: pair < std :: string , T > stringpair < T > ; // Не работает
Однако, если кто-то готов принять stringpair
вместо stringpair
, то можно достичь желаемого результата с помощью typedef внутри неиспользуемого в противном случае шаблонного класса или структуры:
template < typename T > class stringpair { private : // Предотвратить создание экземпляра `stringpair `. stringpair (); public : // Сделать `stringpair :: type` представителем` std :: pair `. typedef std :: pair < std :: string , T > type ; };// Объявляем переменную типа `std :: pair `. stringpair < int > :: type my_pair_of_string_and_int ;
В C ++ 11 шаблонные определения типов добавляются со следующим синтаксисом, для которого требуется using
ключевое слово, а не typedef
ключевое слово. (См. Псевдонимы шаблонов .) [5]
template < typename T > using stringpair = std :: pair < std :: string , T > ;// Объявляем переменную типа `std :: pair `. stringpair < int > my_pair_of_string_and_int ;
Другие языки
В SystemVerilog typedef ведет себя точно так же, как в C и C ++. [6]
Во многих функциональных языках со статической типизацией, таких как Haskell , Miranda , OCaml и т. Д., Можно определять синонимы типов , которые аналогичны определениям типов в C. Пример в Haskell:
тип PairOfInts = ( Int , Int )
В этом примере синоним типа определен PairOfInts
как целочисленный тип.
В Seed7 определение постоянного типа используется для введения синонима типа:
Тип const: myVector - целочисленный массив;
В Swifttypealias
ключевое слово используется для создания typedef:
typealias PairOfInts = ( Инт , Инт )
C # содержит функцию, аналогичную typedef или using
синтаксису C ++. [7] [5]
используя newType = global :: System . Время выполнения . Interop . Маршал ; используя otherType = Перечисления . MyEnumType ; используя StringListMap = System . Коллекции . Универсальный . Словарь < строка , System . Коллекции . Универсальный . Список < строка >>;
В D ключевое слово alias
[8] позволяет создавать синонимы типа или части типа.
struct Foo ( T ) {} псевдоним FooInt = Foo ! int ; псевдоним Fun = int delegate ( int );
Проблемы использования
Керниган и Ричи указали две причины использования typedef. [1] Во-первых, он предоставляет средства, позволяющие сделать программу более переносимой или более простой в обслуживании. Вместо того, чтобы изменять тип при каждом появлении в исходных файлах программы, нужно изменить только один оператор typedef. size_t и ptrdiff_t в
Некоторые программисты выступают против широкого использования определений типов. Большинство аргументов основано на идее, что typedef просто скрывает фактический тип данных переменной. Например, Грег Кроа-Хартман , хакер ядра Linux и разработчик документации, не рекомендует использовать их для чего-либо, кроме объявления прототипов функций. Он утверждает, что такая практика не только излишне запутывает код, но также может привести к тому, что программисты будут случайно неправильно использовать большие структуры, считая их простыми типами. [9]
Смотрите также
- Абстрактный тип данных
- Синтаксис C
Рекомендации
- ^ а б Керниган, Брайан В .; Ричи, Деннис М. (1988). Язык программирования C (2-е изд.). Энглвуд Клиффс, Нью-Джерси: Prentice Hall. п. 147 . ISBN 0-13-110362-8. Проверено 18 июня +2016 .
C предоставляет средство под названием typedef для создания имен новых типов данных. … Следует подчеркнуть, что объявление typedef ни в каком смысле не создает новый тип; он просто добавляет новое имя для некоторого существующего типа.
- ^ "квалификатор константного типа" . cppreference.com . Проверено 20 октября 2020 .
- ^ "спецификатор typedef" . cppreference.com . Проверено 18 июня +2016 .
- ^ Deitel, Paul J .; Дейтель, HM (2007). C как программировать (5-е изд.). Река Аппер Сэдл, Нью-Джерси: Пирсон Прентис Холл. ISBN 9780132404167. Проверено 12 сентября 2012 года .
Имена для структурных типов часто определяются с помощью typedef для создания более коротких имен типов.
- ^ а б «Введите псевдоним, шаблон псевдонима (начиная с C ++ 11) - cppreference.com» . en.cppreference.com . Проверено 25 сентября 2018 .
- ^ Тала, Дипак Кумар. «Типы данных SystemVerilog, часть V» . www.asic-world.com . ASIC World . Проверено 25 сентября 2018 года .
- ^ http://msdn.microsoft.com/en-us/library/aa664765(VS.71).aspx
- ^ "Объявления - язык программирования D" . dlang.org . Проверено 28 мая 2017 .
- ^ Кроа-Хартман, Грег (01.07.2002). «Правильный стиль кодирования ядра Linux» . Linux Journal . Проверено 23 сентября 2007 .
Использование typedef скрывает только реальный тип переменной.