typedef, enum и union
typedef: своё имя для типа
typedef создаёт псевдоним типа. Чаще всего его используют для структур:
typedef struct
{
int id;
int course;
double score;
} Student;
Student s;
Student group[100];
Без typedef пришлось бы писать struct Student каждый раз. typedef не создаёт новый
тип, а только удобное имя.
Можно задавать псевдонимы и для простых типов, но это редко улучшает читаемость:
typedef unsigned long long ull;
typedef double Score;
enum: именованные целые константы
enum задаёт набор именованных целых значений:
typedef enum
{
STATUS_OK,
STATUS_NOT_FOUND,
STATUS_NO_MEMORY,
STATUS_INVALID_INPUT
} Status;
По умолчанию значения идут 0, 1, 2, …. Можно задать явно:
typedef enum
{
VALUE_INT = 1,
VALUE_DOUBLE = 2
} ValueType;
enum удобно использовать для кодов возврата функций:
Status remove_by_id(Node **head, int id)
{
if (head == NULL || *head == NULL)
{
return STATUS_INVALID_INPUT;
}
/* ... */
return STATUS_OK;
}
Внутри enum — целое число, поэтому его можно сравнивать и печатать через %d, но
лучше использовать именованные константы, а не «магические» числа.
union: несколько полей в одной памяти
union похож на структуру, но все его поля занимают одну и ту же область памяти.
В каждый момент времени хранится только одно значение:
typedef union
{
int i;
double d;
} Number;
Number n;
n.i = 42; /* сейчас хранится int */
n.d = 3.14; /* int перезаписан */
Размер union равен размеру самого большого поля (с учётом выравнивания). Читать нужно
то поле, которое было записано последним; иначе результат не определён.
Tagged union: значение + тип
Чтобы понимать, какое поле union действительно хранится, рядом кладут enum-тег:
typedef enum
{
VALUE_INT = 1,
VALUE_DOUBLE = 2
} ValueType;
typedef struct
{
ValueType type;
union
{
int i;
double d;
} as;
} Value;
Создание и вывод:
Value v;
v.type = VALUE_INT;
v.as.i = 42;
if (v.type == VALUE_INT)
{
printf("%d\n", v.as.i);
}
else if (v.type == VALUE_DOUBLE)
{
printf("%.2f\n", v.as.d);
}
Такой тип используется в high-вариантах ЛР3: кольцевой список хранит Value, а type
показывает, как читать as.
Массив значений разных типов
Value values[3];
values[0].type = VALUE_INT;
values[0].as.i = 10;
values[1].type = VALUE_DOUBLE;
values[1].as.d = 2.5;
values[2].type = VALUE_INT;
values[2].as.i = -7;
for (int i = 0; i < 3; i++)
{
if (values[i].type == VALUE_INT)
{
printf("%d %d\n", VALUE_INT, values[i].as.i);
}
else
{
printf("%d %.2f\n", VALUE_DOUBLE, values[i].as.d);
}
}
enum и union в структуре
typedef struct
{
int id;
ValueType type;
union
{
int i;
double d;
} as;
} Record;
Обращение к полям: r.type, r.as.i, r.as.d. Перед чтением as всегда проверяйте
type.
Частые ошибки
| Ошибка | Что не так |
|---|---|
забыли ; после } enum/union/struct | ошибка компиляции |
читают не то поле union | неопределённое поведение |
путают enum и строку | enum — целые константы, не текст |
используют «магические» числа вместо enum | код труднее читать |
typedef struct { ... }; без имени после } | нет псевдонима, если он нужен |
сравнивают union целиком | в C нельзя сравнивать объединения целиком |
Документация
Что запомнить
typedefсоздаёт удобное имя типа, но не новый тип.enum— именованные целые константы; удобен для статусов и типов.unionхранит одно из полей в общей памяти.- Tagged union =
enum-тег +union; перед чтением проверяйте тег. - В lab3
Valueхранитintилиdouble, строк нет.