Поиск…


Вступление

Механизм 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 нужно будет менять каждый раз, а не рассматривать всю программу.

C99

Заголовок <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 имен для указателей на типы функций облегчает жизнь, это также может привести к путанице для других, которые впоследствии будут поддерживать ваш код, поэтому используйте с осторожностью и правильной документацией. См. Также Указатели функций .



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow