typedef

редактировать

typedef - зарезервированное ключевое слово в языках программирования C и C ++. Он используется для создания дополнительного имени ( псевдонима) для другого типа данных, но не создает новый тип, за исключением неясного случая квалифицированного typedef для типа массива, где квалификаторы typedef передаются типу элемента массива. Таким образом, он часто используется для упрощения синтаксиса объявления сложных структур данных, состоящих из типов структур и объединений, но столь же распространен при предоставлении конкретных описательных имен типов для целочисленных типов данных различной длины.

СОДЕРЖАНИЕ

  • 1 Синтаксис
  • 2 Примеры
    • 2.1 Использование документации
    • 2.2 Упрощение типов
    • 2.3 Указатели
    • 2.4 Структуры и указатели на структуры
    • 2.5 Указатели функций
    • 2.6 Массивы
  • 3 типа приведений
  • 4 Использование в C ++
    • 4.1 Использование с шаблонами
  • 5 Другие языки
  • 6 Проблемы использования
  • 7 См. Также
  • 8 ссылки

Синтаксис

Синтаксис объявления typedef:

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 congratulate(int your_score) { if (your_score gt; high_score) { //... } }

может быть выражено объявлением типов, зависящих от контекста:

typedef int km_per_hour; typedef int points; // `km_per_hour` is synonymous with `int` here, and thus, the compiler treats // our new variables as integers. km_per_hour current_speed; points high_score; void congratulate(points your_score) { if (your_score gt; 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; congratulate(km100); }

Упрощение типов

Typedef может использоваться для упрощения объявления составного типа ( структура, объединение ) или типа указателя. Например,

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; // Same as: // int *ptr;

intptrэто новый псевдоним с типом указателя int *. Определение intptr ptr;,, определяет переменную ptrс типом int *. Итак, ptrэто указатель, который может указывать на переменную типа int.

Использование typedef для определения нового типа указателя иногда может привести к путанице. Например:

typedef int *intptr; // Both 'cliff' and 'allen' are of type int*. intptr cliff, allen; // 'cliff2' is of type int*, but 'allen2' is of type int**. intptr cliff2, *allen2; // Same as: // 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; Node *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) { return arg2; } int call_a_func(int (*call_this)(float, int)) { int output = call_this(5.5, 7); return output; } int final_result = call_a_func(amp;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); return output; } int final_result = call_a_func(amp;do_math);

Вот MathFuncновый псевдоним типа. A MathFunc- указатель на функцию, которая возвращает целое число и принимает в качестве аргументов число с плавающей запятой, за которым следует целое число.

Когда функция возвращает указатель на функцию, без typedef это может быть еще более запутанным. Ниже приведен прототип функции signal (3) из FreeBSD :

void (*signal(int sig, void (*func)(int)))(int);

Объявление функции выше является загадочным, поскольку оно не ясно показывает, что функция принимает в качестве аргументов или тип, который она возвращает. Начинающий программист может даже предположить, что функция принимает единственный intаргумент и ничего не возвращает, но на самом деле ей также нужен указатель на функцию и она возвращает другой указатель на функцию. Более чисто можно написать:

typedef void (*sighandler_t)(int); sighandler_t signal(int sig, sighandler_t func);

Массивы

Typedef также можно использовать для упрощения определения типов массивов. Например,

typedef char arrType[6]; arrType arr = {1, 2, 3, 4, 5, 6}; arrType *pArr; // Same as: // char arr[6] = {1, 2, 3, 4, 5, 6}; // char (*pArr)[6];

Вот arrTypeновый псевдоним для char[6]типа, который представляет собой тип массива с 6 элементами. Для arrType *pArr;, pArrэто указатель, указывающий на память char[6]типа.

Приведение типов

Typedef создается с использованием синтаксиса определения типа, но может использоваться, как если бы он был создан с использованием синтаксиса приведения типов. ( Приведение типов изменяет тип данных.) Например, в каждой строке после первой строки:

// `funcptr` is a pointer to a function which takes a `double` and returns an `int`. typedef int (*funcptr)(double); // Valid in both C and C++. funcptr x = (funcptr) NULL; // Only valid in C++. funcptr y = funcptr(NULL); funcptr z = static_castlt;funcptrgt;(NULL);

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

Без typedef, как правило, невозможно взаимозаменяемо использовать синтаксис определения и синтаксис приведения. Например:

void *p = NULL; // This is legal. int (*x)(double) = (int (*)(double)) p; // Left-hand side is not legal. int (*)(double) y = (int (*)(double)) p; // Right-hand side is not legal. int (*z)(double) = (int (*p)(double));

Использование в C ++

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

std::vectorlt;std::pairlt;std::string, intgt;gt; values; for (std::vectorlt;std::pairlt;std::string, intgt;gt;::const_iterator i = values.begin(); i != values.end(); ++i) { std::pairlt;std::string, intgt; const amp; t = *i; //... }

а также

typedef std::pairlt;std::string, intgt; value_t; typedef std::vectorlt;value_tgt; values_t; values_t values; for (values_t::const_iterator i = values.begin(); i != values.end(); ++i) { value_t const amp; t = *i; //... }

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

using value_t = std::pairlt;std::string, intgt;; using values_t = std::vectorlt;value_tgt;;

Использовать с шаблонами

C ++ 03 не предоставляет шаблонных определений типов. Например, чтобы stringpairlt;Tgt;представлять std::pairlt;std::string, Tgt;для каждого типа Tодин не может использовать:

templatelt;typename Tgt; typedef std::pairlt;std::string, Tgt; stringpairlt;Tgt;; // Doesn't work

Однако, если кто-то желает принять stringpairlt;Tgt;::typeвместо stringpairlt;Tgt;, то можно достичь желаемого результата с помощью typedef внутри неиспользуемого в противном случае шаблонного класса или структуры:

templatelt;typename Tgt; class stringpair { private: // Prevent instantiation of `stringpairlt;Tgt;`. stringpair(); public: // Make `stringpairlt;Tgt;::type` represent `std::pairlt;std::string, Tgt;`. typedef std::pairlt;std::string, Tgt; type; }; // Declare a variable of type `std::pairlt;std::string, intgt;`. stringpairlt;intgt;::type my_pair_of_string_and_int;

В C ++ 11 шаблонные определения типов добавляются со следующим синтаксисом, для которого требуется usingключевое слово, а не typedefключевое слово. (См. Псевдонимы шаблонов. )

template lt;typename Tgt; using stringpair = std::pairlt;std::string, Tgt;; // Declare a variable of type `std::pairlt;std::string, intgt;`. stringpairlt;intgt; my_pair_of_string_and_int;

Другие языки

В SystemVerilog typedef ведет себя точно так же, как в C и C ++.

Во многих функциональных языках со статической типизацией, таких как Haskell, Miranda, OCaml и т. Д., Можно определять синонимы типов, которые аналогичны определениям типов в C. Пример в Haskell:

type PairOfInts = (Int, Int)

В этом примере синоним типа определен PairOfIntsкак целочисленный тип.

В Seed7 определение постоянного типа используется для введения синонима типа:

const type: myVector is array integer;

В Swift typealias ключевое слово используется для создания typedef:

typealias PairOfInts = (Int, Int)

C # содержит функцию, аналогичную typedef или usingсинтаксису C ++.

using newType = global::System.Runtime.Interop.Marshal; using otherType = Enums.MyEnumType; using StringListMap = System.Collections.Generic.Dictionarylt;string, System.Collections.Generic.Listlt;stringgt;gt;;

В D ключевое слово aliasпозволяет создавать синонимы типа или частичного типа.

struct Foo(T){} alias FooInt = Foo!int; alias Fun = int delegate(int);

Проблемы использования

Керниган и Ричи указали две причины использования typedef. Во-первых, он предоставляет средства, позволяющие сделать программу более переносимой или более простой в обслуживании. Вместо того, чтобы изменять тип при каждом появлении в исходных файлах программы, нужно изменить только один оператор typedef. size_t и ptrdiff_t в lt;stdlib.hgt; являются такими именами typedef. Во-вторых, определение типа может облегчить понимание сложного определения или объявления.

Некоторые программисты выступают против широкого использования определений типов. Большинство аргументов основано на идее, что typedef просто скрывает фактический тип данных переменной. Например, Грег Кроа-Хартман, хакер ядра Linux и разработчик документации, не рекомендует использовать их для чего-либо, кроме объявления прототипов функций. Он утверждает, что такая практика не только излишне запутывает код, но также может привести к тому, что программисты будут случайно неправильно использовать большие структуры, считая их простыми типами.

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

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

Последняя правка сделана 2023-03-19 09:03:56
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте