stringtranslate.com

определение типа

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и unionC++ называет тег именем класса.

Соответствие между этой функцией 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 или синтаксису usingC++. [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]

Смотрите также

Рекомендации

  1. ^ abc Керниган, Брайан В .; Ричи, Деннис М. (1988). Язык программирования C (2-е изд.). Энглвуд Клиффс, Нью-Джерси: Прентис Холл. п. 147. ИСБН 0-13-110362-8. Проверено 18 июня 2016 г. C предоставляет возможность typedef для создания имен новых типов данных. … Следует подчеркнуть, что объявление typedef ни в каком смысле не создает новый тип; он просто добавляет новое имя для некоторого существующего типа.
  2. ^ «Квалификатор константного типа». cppreference.com . Проверено 20 октября 2020 г.
  3. ^ «Спецификатор typedef (C++)». cppreference.com . Проверено 18 июня 2016 г.
  4. ^ «Объявление typedef (C)» . cppreference.com . Проверено 18 июня 2016 г.
  5. ^ Дейтел, Пол Дж.; Дейтел, Х.М. (2007). C как программировать (5-е изд.). Река Аппер-Сэддл, Нью-Джерси: Пирсон Прентис Холл. ISBN 9780132404167. Проверено 12 сентября 2012 г. Имена структурных типов часто определяются с помощью typedef для создания более коротких имен типов.
  6. ^ ab «Введите псевдоним, шаблон псевдонима (начиная с C++11) — cppreference.com». ru.cppreference.com . Проверено 25 сентября 2018 г.
  7. ^ Тала, Дипак Кумар. «Типы данных SystemVerilog, часть V». www.asic-world.com . ASIC- мир . Проверено 25 сентября 2018 г.
  8. ^ «Устаревшая техническая документация Visual Studio 2003» .
  9. ^ «Объявления — язык программирования D» . dlang.org . Проверено 28 мая 2017 г.
  10. ^ Кроа-Хартман, Грег (1 июля 2002 г.). «Правильный стиль кодирования ядра Linux». Linux-журнал . Проверено 23 сентября 2007 г. Использование typedef только скрывает реальный тип переменной.