C Language
Typedef
Поиск…
Вступление
Механизм typedef
позволяет создавать псевдонимы для других типов. Он не создает новые типы. Люди часто используют typedef
чтобы улучшить переносимость кода, дать псевдонимы структуре или типам объединения или создать псевдонимы для типов функций (или указателей функций).
В стандарте C typedef
классифицируется как «класс хранения» для удобства; это происходит синтаксически, когда могут появляться классы хранения, такие как static
или extern
.
Синтаксис
- typedef existing_name alias_name;
замечания
Недостатки Typedef
typedef
может привести к загрязнению пространства имен в больших программах на C.
Недостатки Typedef Structs
Кроме того, typedef
'd structs без имени тега являются основной причиной ненужного наложения отношений упорядочения между заголовочными файлами.
Рассматривать:
#ifndef FOO_H
#define FOO_H 1
#define FOO_DEF (0xDEADBABE)
struct bar; /* forward declaration, defined in bar.h*/
struct foo {
struct bar *bar;
};
#endif
С таким определением, не используя typedefs
, блок компиляции может включать foo.h
для определения FOO_DEF
. Если он не пытается разыменовать элемент bar
структуры foo
тогда нет необходимости включать файл bar.h
Typedef vs #define
#define
- это #define
C, которая также используется для определения псевдонимов для разных типов данных, аналогичных typedef
но со следующими отличиями:
typedef
ограничивается предоставлением символических имен только для типов, где#define
может использоваться для определения псевдонимов для значений.интерпретация
typedef
выполняется компилятором, тогда как операторы#define
обрабатываются предварительным процессором.Обратите внимание, что
#define cptr char *
за которым следуетcptr a, b;
не делает то же самое, чтоtypedef char *cptr;
а затемcptr a, b;
, С#define
,b
- простая переменнаяchar
, но она также является указателем сtypedef
.
Typedef для структур и союзов
Вы можете дать псевдоним именам для struct
:
typedef struct Person {
char name[32];
int age;
} Person;
Person person;
По сравнению с традиционным способом объявления структур, программистам не требуется иметь struct
каждый раз, когда они объявляют экземпляр этой структуры.
Обратите внимание, что имя Person
(в отличие от struct Person
) не определено до окончательной точки с запятой. Таким образом, для связанных списков и древовидных структур, которые должны содержать указатель на один и тот же тип структуры, вы должны использовать либо:
typedef struct Person {
char name[32];
int age;
struct Person *next;
} Person;
или же:
typedef struct Person Person;
struct Person {
char name[32];
int age;
Person *next;
};
Использование typedef
для типа union
очень похоже.
typedef union Float Float;
union Float
{
float f;
char b[sizeof(float)];
};
Аналогичную структуру можно использовать для анализа байтов, которые составляют значение float
.
Простое использование Typedef
Для предоставления коротких имен типу данных
Вместо:
long long int foo;
struct mystructure object;
можно использовать
/* write once */
typedef long long ll;
typedef struct mystructure mystruct;
/* use whenever needed */
ll foo;
mystruct object;
Это уменьшает объем ввода, если тип используется многократно в программе.
Улучшение переносимости
Атрибуты типов данных различаются для разных архитектур. Например, int
может быть двухбайтным типом в одной реализации и 4-байтным типом в другом. Предположим, что программа должна использовать 4-байтовый тип для правильной работы.
В одной реализации пусть размер int
равен 2 байтам, а long
- 4 байта. В другом случае пусть размер int
равен 4 байтам, а long
- 8 байтов. Если программа написана с использованием второй реализации,
/* program expecting a 4 byte integer */
int foo; /* need to hold 4 bytes to work */
/* some code involving many more ints */
Для запуска программы в первой реализации все объявления int
должны быть изменены на long
.
/* program now needs long */
long foo; /*need to hold 4 bytes to work */
/* some code involving many more longs - lot to be changed */
Чтобы этого избежать, можно использовать typedef
/* program expecting a 4 byte integer */
typedef int myint; /* need to declare once - only one line to modify if needed */
myint foo; /* need to hold 4 bytes to work */
/* some code involving many more myints */
Тогда только инструкция typedef
нужно будет менять каждый раз, а не рассматривать всю программу.
Заголовок <stdint.h>
и связанный с <inttypes.h>
заголовок <inttypes.h>
определяют стандартные имена типов (с использованием typedef
) для целых чисел различного размера, и эти имена часто являются лучшим выбором в современном коде, для которого требуются целые числа фиксированного размера. Например, uint8_t
представляет собой неподписанный 8-разрядный целочисленный тип; int64_t
- это подписанный 64-разрядный целочисленный тип. Тип uintptr_t
представляет собой целочисленный тип без знака, достаточно большой, чтобы содержать любой указатель на объект. Эти типы теоретически необязательны, но редко они недоступны. Существуют такие варианты, как uint_least16_t
(наименьший беззнаковый целочисленный тип с не менее 16 битами) и int_fast32_t
(самый быстрый тип целочисленного типа со int_fast32_t
не менее 32 бит). Кроме того, intmax_t
и uintmax_t
являются наибольшими целыми типами, поддерживаемыми реализацией. Эти типы являются обязательными.
Чтобы указать использование или улучшить читаемость
Если набор данных имеет определенную цель, можно использовать typedef
чтобы дать ему значимое имя. Более того, если свойство данных изменяется так, что базовый тип должен измениться, нужно будет изменить только оператор typedef
, а не рассматривать всю программу.
Typedef для указателей функций
Мы можем использовать typedef
для упрощения использования указателей на функции. Представьте, что у нас есть некоторые функции, имеющие одну и ту же подпись, которые используют свой аргумент для распечатки чего-то по-разному:
#include<stdio.h>
void print_to_n(int n)
{
for (int i = 1; i <= n; ++i)
printf("%d\n", i);
}
void print_n(int n)
{
printf("%d\n, n);
}
Теперь мы можем использовать typedef
для создания названного типа указателя функции, называемого printer:
typedef void (*printer_t)(int);
Это создает тип с именем printer_t
для указателя на функцию, которая принимает единственный аргумент int
и ничего не возвращает, что соответствует сигнатуре функций, которые мы имеем выше. Чтобы использовать его, мы создаем переменную созданного типа и присваиваем ей указатель на одну из рассматриваемых функций:
printer_t p = &print_to_n;
void (*p)(int) = &print_to_n; // This would be required without the type
Затем для вызова функции, на которую указывает переменная указателя функции:
p(5); // Prints 1 2 3 4 5 on separate lines
(*p)(5); // So does this
Таким образом, typedef
позволяет упростить синтаксис при работе с указателями функций. Это становится более очевидным, когда указатели на функции используются в более сложных ситуациях, таких как аргументы для функций.
Если вы используете функцию, которая принимает указатель на функцию в качестве параметра без определенного типа указателя функции, определение функции будет,
void foo (void (*printer)(int), int y){
//code
printer(y);
//code
}
Однако, с typedef
это:
void foo (printer_t printer, int y){
//code
printer(y);
//code
}
Аналогично, функции могут возвращать указатели на функции и снова, использование typedef
может упростить синтаксис при этом.
Классическим примером является signal
функция от <signal.h>
. Декларация для него (из стандарта C):
void (*signal(int sig, void (*func)(int)))(int);
Это функция, которая принимает два аргумента - int
и указатель на функцию, которая принимает int
как аргумент и ничего не возвращает - и которая возвращает указатель на функцию как свой второй аргумент.
Если мы определили тип SigCatcher
как псевдоним для указателя на тип функции:
typedef void (*SigCatcher)(int);
то мы могли бы объявить signal()
используя:
SigCatcher signal(int sig, SigCatcher func);
В целом это легче понять (хотя стандарт C не решил определить тип для выполнения задания). Функция signal
принимает два аргумента: int
и SigCatcher
, и возвращает SigCatcher
где SigCatcher
является указателем на функцию, которая принимает аргумент int
и ничего не возвращает.
Хотя использование typedef
имен для указателей на типы функций облегчает жизнь, это также может привести к путанице для других, которые впоследствии будут поддерживать ваш код, поэтому используйте с осторожностью и правильной документацией. См. Также Указатели функций .