Структуры в C (struct): определение, инициализация, передача в функции, указатели и битовые поля
Что такое структура?
Структура (struct) — пользовательский тип, объединяющий переменные разных типов под одним именем. Позволяет моделировать записи (records) — сущности с несколькими атрибутами.
Объявление и определение
// Тег структуры: struct Book
struct Book {
char title[100];
char author[50];
int year;
double price;
bool available;
}; // точка с запятой обязательна!
// Переменные структуры
struct Book book1; // неинициализирована
struct Book book2 = { // инициализация списком (порядок полей!)
"C Programming", "K&R", 1978, 45.99, true
};
struct Book book3 = { // designated initializers (C99) — порядок не важен
.title = "Modern C",
.author = "Jens Gustedt",
.year = 2020,
.price = 59.99
};
Доступ к полям
- Через точку
.для переменной структуры:book1.year = 2024; - Через стрелку
->для указателя:ptr->year = 2024;(эквивалент(*ptr).year).
Структуры и функции
Передача по значению (копия всей структуры)
void print_book(struct Book b) { // копируется вся структура!
printf("%s by %s (%d), $%.2f\n", b.title, b.author, b.year, b.price);
}
// Вызов: print_book(book1);
Плюсы: простота, иммутабельность аргумента. Минусы: накладные расходы на копирование больших структур.
Передача по указателю (рекомендуется для больших структур)
void print_book_const(const struct Book *b) { // const — обещание не менять
printf("%s by %s (%d), $%.2f\n", b->title, b->author, b->year, b->price);
}
void apply_discount(struct Book *b, double percent) {
b->price *= (1.0 - percent / 100.0);
}
// Вызов: print_book_const(&book1); apply_discount(&book1, 10.0);
Указатели на структуры
struct Book *ptr = &book1;
ptr->year = 2025; // удобнее, чем (*ptr).year
(*ptr).price = 30.0; // тоже работает, но скобки обязательны
Массивы структур
struct Book library[100];
library[0] = (struct Book){"Book A", "Author A", 2020, 10.0, true}; // compound literal (C99)
library[1].title = "Book B";
Вложенные структуры
struct Date { int day, month, year; };
struct Employee {
char name[50];
struct Date birth_date; // вложенная структура (по значению)
struct Date *hire_date; // указатель на структуру
};
struct Employee emp = {
.name = "John",
.birth_date = {15, 5, 1990}, // инициализация вложенной
.hire_date = &(struct Date){1, 1, 2020} // compound literal для указателя
};
Самореференциальные структуры (для списков, деревьев)
struct Node {
int data;
struct Node *next; // указатель на ТОТ ЖЕ тип
};
struct Node *head = NULL;
head = malloc(sizeof(struct Node));
head->data = 42;
head->next = NULL;
typedef для удобства
// Обычный способ
typedef struct {
char title[100];
char author[50];
int year;
} Book; // теперь можно: Book b;
// Или с тегом (для самореференциальных)
typedef struct Node Node;
struct Node {
int data;
Node *next; // используем typedef имя
};
Выравнивание и упаковка (padding/packing)
Компилятор выравнивает поля по границам их типов для быстрого доступа. Это создаёт «дырки» (padding).
struct Padded {
char c; // 1 байт
// 3 байта padding
int i; // 4 байта (выровнено по 4)
// 0 байт padding
double d; // 8 байт (выровнено по 8)
}; // размер: 24 байта (1+3+4+8+8? нет, 1+3+4+8=16, но double требует 8-байтовое выравнивание структуры -> 24)
struct Packed {
char c;
int i;
double d;
} __attribute__((packed)); // GCC/Clang атрибут: без padding
// Размер: 1+4+8=13, но доступ к int/double может быть медленным или вызвать SIGBUS на некоторых архитектурах!
Правило: располагайте поля от больших к маленьким для минимизации padding:
struct Optimal {
double d; // 8 байт, offset 0
int i; // 4 байта, offset 8
char c; // 1 байт, offset 12
// 3 байта padding до размера, кратного 8 (alignof(max field))
}; // размер: 16 байт
Битовые поля (Bit Fields)
Позволяют упаковать несколько флагов/маленьких значений в одно машинное слово.
struct Flags {
unsigned int is_active : 1; // 1 бит
unsigned int is_dirty : 1;
unsigned int mode : 3; // 3 бита (0-7)
unsigned int reserved : 29; // остаток 32-битного слова
}; // размер: 4 байта (обычно)
struct Flags f = {.is_active = 1, .mode = 5};
if (f.is_active) { ... }
f.mode = 3;
Ограничения:
- Только
int,signed int,unsigned int(C23: любой целочисленный). - Нельзя взять адрес (
&f.is_active— ошибка). - Порядок бит в памяти — implementation-defined.
- Не переносимы между архитектурами (endianness, layout). Используйте для флагов, сетевых протоколов, железа.
Неполные типы (opaque pointers / forward declaration)
Скрытие реализации (инкапсуляция):
// api.h
struct Context; // неполный тип
typedef struct Context Context;
Context *ctx_create(void);
void ctx_do_work(Context *ctx);
void ctx_destroy(Context *ctx);
// api.c
#include "api.h"
struct Context { // полное определение только здесь
int secret_data;
void *handle;
};
Context *ctx_create() { ... }
Клиентский код видит только указатель Context*, не значащий внутренностей.
Пример: комплексная структура с функциями
typedef struct {
double re, im;
} Complex;
Complex c_add(Complex a, Complex b) {
return (Complex){a.re + b.re, a.im + b.im};
}
Complex c_mul(Complex a, Complex b) {
return (Complex){
a.re * b.re - a.im * b.im,
a.re * b.im + a.im * b.re
};
}
void c_print(Complex c) {
printf("%.2f %s %.2fi\n", c.re, (c.im >= 0 ? "+" : ""), c.im);
}
Язык C
- Структуры C++ против классов: практическое руководство для разработчиков встроенных систем
- Семафоры в Nucleus RTOS: Утилитарные сервисы и структуры данных
- Рецензия: «Оцифровывайся или умирай» — блупринт для IoT-драйвенной трансформации
- Сохранение человечности в эпоху разрушительных технологий
- Революция в хранилищах данных: магнитные наноструктуры с мантиями невидимости нового поколения
- Топ-17 книг по программированию для прокачки навыков кодинга в 2021 году
- Структуры данных в Java: основные классы и Collections Framework
- typedef в C: создание псевдонимов типов, упрощение кода и отличия от #define
- Структуры данных в C++: массивы, структуры и эффективное программирование
- Структуры в C#: определение, особенности и отличия от классов